前言
学习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(); }