前言
學習ASP.NETCore,原鏈接
https://www.cnblogs.com/laozhang-is-phi/p/9511869.html
原教程是Core2.2,后期也升級到了Core3.0,但是文章中和GitHub的代碼感覺有些亂,一直對應不上,
我創建的項目是Core3.0,而在Swagger中使用JWT一直訪問401,此處做個筆記,供以后學習時查看。
參考博文,原鏈接
https://www.cnblogs.com/CreateMyself/p/11123023.html
步驟
默認映射方式給移除掉
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
Swagger響應頭
這里需要Nuget引用Swashbuckle.AspNetCore.Filters,oauth2需要寫死,SecurityRequirementsOperationFilter中默認securitySchemaName="oauth2";
未添加該配置時,Bearer一直無法加入到JWT發起的Http請求的頭部,無論怎么請求都會是401;
用Postman在Authorization添加了Bearer,就會正常響應,
#region Token綁定到ConfigureServices // 在header中添加token,傳遞到后台 c.OperationFilter<SecurityRequirementsOperationFilter>(); c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JWT授權(數據將在請求頭中進行傳輸) 直接在下框中輸入Bearer {token}(注意兩者之間是一個空格)\"", Name = "Authorization",//jwt默認的參數名稱 In = ParameterLocation.Header,//jwt默認存放Authorization信息的位置(請求頭中) Type = SecuritySchemeType.ApiKey }); #endregion
啟用權限授權認證服務
//JWT服務配置 //讀取配置文件 var audienceConfig = Configuration.GetSection("Audience"); var symmetricKeyAsBase64 = audienceConfig["Secret"]; services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { o.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(symmetricKeyAsBase64)),//參數配置在下邊 ValidateIssuer = true, ValidIssuer = audienceConfig["Issuer"],//發行人 ValidateAudience = true, ValidAudience = audienceConfig["Audience"],//訂閱人 ValidateLifetime = true, //ClockSkew = TimeSpan.Zero,//這個是緩沖過期時間,也就是說,即使我們配置了過期時間,這里也要考慮進去,過期時間+緩沖,默認好像是7分鍾,你可以直接設置為0 ClockSkew = TimeSpan.Zero, RequireExpirationTime = true, }; });
Configure配置
這里的順序,必須嚴格遵守
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); //啟用認證中間件, app.UseAuthentication(); //啟用授權中間件, app.UseAuthorization(); #region swagger // Enable middleware to serve generated Swagger as a JSON endpoint. app.UseSwagger(); // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), // specifying the Swagger JSON endpoint. app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); }); #endregion app.UseEndpoints(endpoints => { //endpoints.MapControllers(); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
授權方法
這里的區別,jwt對象,多加了audience和expires屬性。
audience:就是配置的值,
expires:JWT過期時間,經過測試,JWT過期時間=expires + ClockSkew。並不是claims中的JwtRegisteredClaimNames.Exp去控制的過期時間
public static string IssueJWT(TokenModel tokenModel, TimeSpan expiresSliding, TimeSpan expiresAbsoulte) { var Issuer = "Blog.Core"; var Audience = "wr"; var Secret = "sdfsdfsrty45634kkhllghtdgdfss345t678fs"; var dateTime = DateTime.UtcNow; var claims = new Claim[] { //下邊為Claim的默認配置 new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"), new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") , //這個就是過期時間,目前是過期100秒,可自定義,注意JWT有自己的緩沖過期時間 new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(180)).ToUnixTimeSeconds()}"), new Claim(JwtRegisteredClaimNames.Iss,Issuer), new Claim(JwtRegisteredClaimNames.Aud,Audience), //這個Role是官方UseAuthentication要要驗證的Role,我們就不用手動設置Role這個屬性了 new Claim(ClaimTypes.Role,tokenModel.Role), new Claim(ClaimTypes.Name, tokenModel.Uname), new Claim(JwtRegisteredClaimNames.Email, tokenModel.EMail), new Claim(JwtRegisteredClaimNames.Sub,tokenModel.Sub), }; //秘鑰 var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Secret)); var jwt = new JwtSecurityToken( issuer: Issuer, audience: Audience, claims: claims, expires: DateTime.Now.AddMinutes(1), signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256) ); var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); return encodedJwt; }
appsetting.json
"Audience": { "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //不要太短,16位+ "SecretFile": "C:\\my-file\\blog.core.audience.secret.txt", //安全。內容就是Secret "Issuer": "Blog.Core", "Audience": "wr" }
Postman測試
Authorization => Bearer Token => Token,這里輸入登錄時生成的Token值,不需要帶Bearer 前綴
Postman 示例
Swagger示例
Fiddler監視
Roles配置權限
用戶的Role,與訪問接口配置的Roles不一致,也就是沒有訪問權限,訪問時會響應為403
總結
調用接口一直401
JWT配置需要驗證的東西,
一直401,可能是JWT中未包含上面配置的全部參數
相比較之前,又傳遞了audience和expires參數,這樣訪問接口驗證必傳參數才能通過。
授權
三種方式
(1)基於角色
(2)基於Claim聲明
(3)基於自定義的類
目前,前2個都測試通過的,第三個需要配合創建其他的東西,未實現。
services.AddAuthorization(options => { //1.基於角色 options.AddPolicy("Client", policy => policy.RequireRole("Client").Build()); options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build()); //Client或者Admin options.AddPolicy("ClientOrAdmin", policy => policy.RequireRole("Client", "Admin").Build()); //Client並且Admin options.AddPolicy("ClientAndAdmin", policy => policy.RequireRole("Client").RequireRole("Admin").Build()); //2.基於聲明 options.AddPolicy("AdminClaim2", policy => policy.RequireClaim(ClaimTypes.Name, "Yasuo", "Leesnn").Build()); //3.基於需要Requirement //options.AddPolicy("AdminRequirement", policy => policy.Requirements.Add(new AdminRequirement() { UName = "Kate" })); });
/// <summary> /// 獲取數據,需要授權 /// </summary> /// <param name="name"></param> /// <returns></returns> [Authorize(Policy = "AdminClaim2")] [HttpPost("{name}")] public string PostUser(string name) { var sub = User.FindFirst(d => d.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name")?.Value; name += "---" + sub ?? ""; return name + DateTime.Now.ToLongTimeString(); }