今天的內容是授權模式中的簡化模式,還是先看以下授權流程圖:
在這種模式中我們將與OpenID結合使用,所以首先我們要了解OpenID和OAuth的區別,關於他們的區別,在我上一篇博客《理解OpenID和OAuth的區別》中做了的一些簡要的介紹,這裏不再多說。
授權服務器我們還是在之前的基礎上改動,首先我們需要在Config類裏面添加對OpenID Connect Identity Scopes的支持,與OAuth2.0相比,OIDC(OpenID Connect)同樣需要Scopes的概念,他也需要保護Scopes範圍的內容而也需要讓用戶去訪問的內容,但是OIDC中這裏的範圍並不是用戶要訪問的API,而是用戶標識、用戶名、Email等信息。
添加對OpenId(SubjectId) Profile(first name last name etc...)的支持範圍,並返回一個IdentityReSource的集合,添加以下代碼:
//添加對OpenID Profile範圍的支持
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
再天際一個客戶端,基於Implicit授權模式,添加以下代碼:
new Client(){
ClientId="impClient",
AllowedGrantTypes=GrantTypes.Implicit,
//用於登錄成功後的RedirectUri
RedirectUris={"http://localhost:5004/signin-oidc"},
//用於註銷後的RedirectUri
PostLogoutRedirectUris={"http://localhost:5004/signout-callback-oidc"},
//允許訪問的範圍
AllowedScopes={
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}
}
下面我們還要將IdentityResource注入到IdentityServer中,在ConfigureService中,添加:
.AddInMemoryIdentityResources(Config.GetIdentityResources())
修改返回的TestUser的方法,在每一個用戶中添加一個聲明屬性的集合,返回一個name和address信息,改動如下:
return new List<TestUser>{
new TestUser(){
SubjectId="1",
Username="allen",
Password="123456",
Claims=new List<Claim>
{
new Claim("name","Allen"),
new Claim("address","http://allen.com")
}
},
new TestUser(){
SubjectId="2",
Username="alisa",
Password="123456",
Claims=new List<Claim>
{
new Claim("name","Alisa"),
new Claim("address","http://alisa.com")
}
}
OIDC所需的所有協議已經內置在IdentityServer4中了,需要爲登錄、註銷、同意授權內容以及錯誤等提供UI的部分,IdentityServer4提供了一個簡單的基於MVC的UI界面,我們可以直接download到我們的服務端裏,在控制檯使用命令:
如果你是Windows系統,使用以下命令:
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.ps1'))
如果你是MacOS或者Linux用戶,使用:
\curl -L https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.sh | bash
執行完成後,會發現多了一個QuickStart文件夾,放置了所用到的Controller,Views文件夾下也放置了對應的視圖,
由於我創建的是WebApi項目,所以還要在StartUp.cs裏面將原來我們註釋掉的app.UseMvc()解開,另外在配置默認路由前還要添加使用靜態文件,以使用我們的靜態文件夾下的樣式,app.UseStaticFiles(); 以上內容都修改完成後,下面我們新建一個Mvc的項目,去請求我們的授權服務器,如何創建不再贅述,創建完成後添加對OIDC認證的支持,在ConfigureServices的方法中添加以下代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.IdentityModel.Tokens.Jwt;
namespace MvcClient
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
//添加認證服務到DI中,
services.AddAuthentication(options=>{
//使用Cookies作爲唯一的認證用戶的主要手段
options.DefaultScheme="Cookies";
//需要用戶登錄纔可進入該應用程序,使用OpenID Connect scheme
options.DefaultChallengeScheme="oidc";
})
//添加可以處理Cookie的處理程序
.AddCookie("Cookies")
//配置OpenID Connect協議
.AddOpenIdConnect("oidc",options=>{
//OIDC協議執行完成,發佈Cookie
options.SignInScheme="Cookies";
//授權服務器地址
options.Authority="http://localhost:5000";
options.RequireHttpsMetadata=false;
//指定客戶端Id
options.ClientId="impClient";
//保存令牌在Cookie中
options.SaveTokens=true;
});
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (!env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
//添加認證中間件
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
注意配置啓動時將MvcClient啓動端口改爲5004,因爲我在服務端指定的客戶端的端口爲5004,接着我們添加一個Action,使他需要進行授權訪問,
[Authorize]
public IActionResult Authorize()
{
return View();
}
並添加對應的視圖,展示用戶信息:
@{
ViewData["Title"] = "Authorize";
}
<h2>Claims:</h2>
<dl>
@foreach (var claim in User.Claims)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
</dl>
至此,我們的配置就完成了,下面我們運行看下效果,首先run下服務端,再啓動我們的客戶端,客戶端點擊Authorize菜單:
這個就對應我們那個需要QQ登錄的界面了,需要通過認證,我們輸入我們事先配置的用戶名和密碼,allen 123456,
到consent頁面是要通過我們的授權,將我們用戶信息共享給客戶端,現在我們選擇用戶profile,點擊允許:
當我們點擊不允許讀取用戶信息的時候:
在不允許讀取的情況下,我們是無法看到用戶名的。下面我們再添加一個註銷登錄的方法,
//註銷登錄
public async Task LogOut()
{
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
執行退出:
至此整個Implicit授權模式的過程就進行完了。
掃描二維碼關注我的公衆號,共同學習,共同進步!