上節以對話形式,大概說了幾種客戶端授權模式的原理,這節重點介紹Hybrid模式在MVC下的使用。且為實現IdentityServer4從資料庫獲取User進行驗證,並對Claim進行許可權設置打下基礎(第五節介紹)。 本節內容比較多,且涉及一、二節的內容,如有不懂,可先熟悉一、二節知識。 一、新建授權 ...
上節以對話形式,大概說了幾種客戶端授權模式的原理,這節重點介紹Hybrid模式在MVC下的使用。且為實現IdentityServer4從資料庫獲取User進行驗證,並對Claim進行許可權設置打下基礎(第五節介紹)。
本節內容比較多,且涉及一、二節的內容,如有不懂,可先熟悉一、二節知識。
一、新建授權服務,命名為AuthServer
(1)新建Web API項目,不用配置HTTPS,不進行身份驗證。
設置成控制台方式運行,埠設為5000。
安裝IdentityServer4包
在Config.cs類中,添加如下代碼:
public class Config { public static List<TestUser> GetUsers() { return new List<TestUser> { new TestUser { SubjectId = "1", Username = "test", Password = "123", Claims = new List<Claim> { new Claim("role", "user") } }, new TestUser { SubjectId = "2", Username = "admin", Password = "123", Claims = new List<Claim> { new Claim("role", "admin") } } }; } public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), //new IdentityResource("roles","role",new List<string>{ "role"}) }; } public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("api1", "My API") //new ApiResource("api1", "My API",new List<string>(){ "role"}) }; } // clients want to access resources (aka scopes) public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "AuthServer", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" }, Claims= new List<Claim>(){new Claim("role","AuthServer") }, ClientClaimsPrefix = "" }, // OpenID Connect implicit flow client (MVC) new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Hybrid, ClientSecrets = { new Secret("secret".Sha256()) }, // where to redirect to after login RedirectUris = { "http://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, //"roles" } } }; } }
這裡IdentityResource映射於那些關於用戶信息的scope, ApiResource映射於API資源的scopes。
(2)打開Startup.cs,在ConfigureServices裡面調用AddIdentityServer來把Identity Server註冊到ASP.NET Core的容器裡面;隨後我調用了AddDeveloperSigningCredentials方法,它會創建一個用於對token簽名的臨時密鑰材料(但是在生產環境中應該使用可持久的密鑰材料)
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddIdentityServer() .AddDeveloperSigningCredential() .AddTestUsers(Config.GetUsers()) .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()); }
(3)打開Configure方法,把IdentityServer添加到ASP.NET Core的管道里。
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); //MVC配置 app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); }
(4)然後下載登錄用的UI: https://github.com/IdentityServer/IdentityServer4.Quickstart.UI
把圖中三個文件複製到AuthServer項目目錄下。
複製完後項目如下:
二、新建MVC客戶端,命名為MvcClient
(1)設置埠為5002。
修改Start.cs的ConfigureServices方法為:
public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies", options => { //無許可權,顯示的頁面 options.AccessDeniedPath = "/Authorization/AccessDenied"; }) .AddOpenIdConnect("oidc", options => { options.SignInScheme = "Cookies"; options.Authority = "http://localhost:5000"; options.RequireHttpsMetadata = false; options.ClientId = "mvc"; options.ResponseType = "code id_token"; options.Scope.Clear(); options.Scope.Add("openid"); options.Scope.Add("profile"); //options.Scope.Add("roles"); options.SaveTokens = true; options.ClientSecret = "secret"; options.GetClaimsFromUserInfoEndpoint = true; //options.ClaimActions.MapUniqueJsonKey("role", "role"); //options.TokenValidationParameters = new TokenValidationParameters //{ // NameClaimType = JwtClaimTypes.GivenName, // RoleClaimType = JwtClaimTypes.Role //}; }); }
AddAuthentication方法來添加和配置身份認證中間件。這裡使用Cookie作為驗證用戶的首選方式,而DefaultScheme = "Cookies",這個"Cookies"字元串是可以任意填寫的,只要與後邊的一致即可。但是如果同一個伺服器上有很多應用的話,這個Scheme的名字不能重覆。
DefaultChanllangeScheme設為"oidc", 這個名字與後邊配置OpenIdConnect的名字要一樣. 當用戶需要登陸的時候, 將使用的是OpenId Connect Scheme。
AddCookie其參數是之前配置的DefaultScheme名稱,這配置了Cookie的處理者,並讓應用程式為我們的DefaultScheme啟用了基於Cookie的身份認證。一旦ID Token驗證成功並且轉化為Claims身份標識後,這些信息就將會保存於被加密的Cookie里。
AddOpenIdConnect方法添加了對OpenID Connect流程的支持,它讓配置了用來執行OpenId Connect 協議的處理者。這個處理者會負責創建身份認證請求,Token請求和其它請求,並負責ID Token的驗證工作。它的身份認證scheme就是之前配置的"oidc",它的意思就是如果該客戶端的某部分要求身份認證的時候,OpenID Connect將會作為預設方案被觸發(因為之前設置的DefaultChallengeScheme是"oidc", 和這裡的名字一樣)。
SignInScheme和上面的DefaultScheme一致,它保證身份認證成功的結果將會被保存在方案名為"Cookies"的Cookie里。
Authority就是Identity Provider的地址。
ClientId和Secret要與IdentityProvider裡面的值一樣。
請求的Scope有openid和profile,其實中間件預設也包括了這些scope,但是寫出來更明確一些。
SaveTokens=true,表示允許存儲從Identity Provider那裡獲得的tokens。
(2)修改Configure方法為:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseAuthentication(); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); }
(3)然後對HomeController加上身份驗證。[Authorize]
(4)再修改About的頁面,顯示User的Claim信息。
@{ ViewData["Title"] = "About"; } <h2>@ViewData["Title"]</h2> @*<dt>Access Token</dt> <dd>@ViewData["AccessToken"]</dd>*@ <dl> @foreach (var claim in User.Claims) { <dt>@claim.Type</dt> <dd>@claim.Value</dd> } </dl>
(5)現在,可以運行AuthServer和MvcClient項目了。
(6)輸入Config文件中的TestUser的用戶,密碼都設為123,點擊Login
允許授權
查看About頁面,顯示了user相關的claim信息。
(7)當然,登出功能還沒實現,這裡先實現登出。打開圖中cshtml文件
添加如下代碼:
@if (User.Identity.IsAuthenticated) { <li><a asp-area="" asp-controller="Home" asp-action="Logout">Logout</a></li> }
然後在HomeController控制器中添加Logout方法
public async Task Logout()
{
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
首先要清除本地的Cookie,這個Cookie的名字要與之前配置的預設方案里的名字一致,這一步就相當於登出MVC客戶端。
後一行代碼的作用是跳轉回到Identity Provider,然後用戶可以繼續登出IDP, 也就是IDP會清除它的Cookie。
(8)接著在AuthServer中的Quickstart/Account/AccountOptions實現自動跳轉回登錄頁面。
好了,登錄登出實現完了,我們接著實現Claim許可權限制。
三、為MVC客戶端設置Claim身份驗證
(1)添加TestUser的Claim中Type為role
(2)定義用戶信息scope的role信息
第一個參數是scope的名字,第二個參數是scope的顯示名,第三個參數是它所包含的claim類型,這裡就是“role”。
(3)然後還需要客戶端允許請求“roles”這個scope
(4)MVC客戶端的配置,打開MVC的startup,添加“roles”這個scope:options.Scope.Add("roles");
把role claim 映射到User.Claims里:options.ClaimActions.MapUniqueJsonKey("role", "role");
role claim映射成ASP.NET Core MVC可以識別的角色Roles。
options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = JwtClaimTypes.GivenName, RoleClaimType = JwtClaimTypes.Role };
這樣MVC中的role就可以識別User.Claims的role了。
(6)最後在MvcClient項目HomeController中 About前,加上role為admin身份驗證。[Authorize(Roles ="admin")]
然後運行,先用test賬號登錄進行驗證。
發現點About頁面沒有許可權進不去
然後登出,換admin賬號登錄
User.Claims的role成功被MVC中角色role識別,展示About頁面。
這節主要介紹Hybrid在MVC下的使用,包括User的登錄登出和Claim對MVC的身份授權。
然而,這隻是針對記憶體用戶TestUser進行操作的,顯示實際項目中不能滿足我們需求。下節將在本節的基礎上介紹如何實現IdentityServer4從資料庫獲取User進行驗證並對Claim進行身份驗證。
參考博客: https://www.cnblogs.com/cgzl/p/9268371.html
源碼地址:https://github.com/Bingjian-Zhu/Mvc-HybridFlowV0.git