NetCore JWT token
在netcore中jwt使用场景很多,网上有很多的资料,这里不再累述,之后有机会会单独介绍,今天主要以实战为主。
1、create jwt token

1 public interface IJwtTokenService 2 { 3 string GetJwtToken(); 4 } 5 6 public class JwtTokenService : IJwtTokenService 7 { 8 private readonly IConfiguration _config; 9 public JwtTokenService(IConfiguration config) 10 { 11 this._config = config; 12 } 13 public string GetJwtToken() 14 { 15 var claims = new List<Claim>() 16 { 17 new Claim(JwtRegisteredClaimNames.Sub,"jwtsubvalue"), 18 new Claim(JwtRegisteredClaimNames.Name,"jwtnamevalue"), 19 new Claim("Scope","API"), 20 new Claim("Role","Admin") 21 }; 22 var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JwtToken:SecretKey"])); 23 var credentials = new SigningCredentials(securityKey,SecurityAlgorithms.HmacSha256); 24 var issuer = _config["JwtToken:Issuer"]; 25 var audience = _config["JwtToken:Audience"]; 26 var expires = DateTime.Now.AddMinutes(Convert.ToDouble(_config["JwtToken:ExpiredMinutes"])); 27 28 var jwtToken = new JwtSecurityToken(issuer, audience, claims: claims, notBefore: DateTime.Now, expires: expires, signingCredentials: credentials); 29 return new JwtSecurityTokenHandler().WriteToken(jwtToken); 30 } 31 }
2、configuration中所需配置信息
1 "JwtToken": { 2 "SecretKey": "12345678901234567890123456789012", 3 "Issuer": "https://localhost:5000", 4 "Audience": "https://localhost:5000", 5 "ExpiredMinutes": 10 6 }
3、在Program中配置authentication

1 var configuration = builder.Configuration; 2 builder.Services.AddAuthentication(options => 3 { 4 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 5 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 6 }) 7 .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => 8 { 9 options.TokenValidationParameters = new TokenValidationParameters 10 { 11 ValidateIssuer = true, 12 ValidateAudience = true, 13 ValidateLifetime = true, 14 ValidateIssuerSigningKey = true, 15 ValidIssuer = configuration["JwtToken:Issuer"], 16 ValidAudience = configuration["JwtToken:Audience"], 17 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtToken:SecretKey"])) 18 }; 19 options.Events = new JwtBearerEvents 20 { 21 OnTokenValidated = context => 22 { 23 var token = context.SecurityToken as JwtSecurityToken; 24 var identity = context.Principal?.Identity as ClaimsIdentity; 25 if (!string.IsNullOrEmpty(token?.Issuer)) 26 context.Success(); 27 else 28 context.Fail($"Token invalid"); 29 return Task.CompletedTask; 30 }, 31 OnAuthenticationFailed = context => 32 { 33 context.Fail(context.Exception); 34 return Task.CompletedTask; 35 }, 36 OnForbidden = context => 37 { 38 context.Fail("403 forbidden"); 39 return Task.CompletedTask; 40 }, 41 OnChallenge = context => 42 { 43 var error = context.Error; 44 return Task.CompletedTask; 45 }, 46 OnMessageReceived = context => 47 { 48 var token = context.Token; 49 return Task.CompletedTask; 50 } 51 }; 52 //Configure authentication scheme forwarding to another scheme 53 options.ForwardDefaultSelector = context => 54 { 55 if (context.Request.Headers.TryGetValue("x-api-type", out Microsoft.Extensions.Primitives.StringValues uiTokenType)) 56 return "CustomScheme"; 57 return JwtBearerDefaults.AuthenticationScheme; 58 }; 59 }).AddJwtBearer("CustomScheme", options => 60 { 61 options.Authority = configuration["JwtToken:Issuer"]; 62 options.TokenValidationParameters = new TokenValidationParameters 63 { 64 ValidateIssuer = true, 65 ValidIssuer = configuration["JwtToken:Issuer"] 66 }; 67 options.Events = new JwtBearerEvents 68 { 69 OnTokenValidated = context => 70 { 71 context.Success(); 72 return Task.CompletedTask; 73 } 74 }; 75 });
4、在请求管道中应用authentication
app.UseAuthentication();
5、添加测试controller,指定[Authorize] atrribute

1 [Authorize] 2 [HttpGet("all")] 3 public IActionResult Get([FromServices] IConfiguration configuration) 4 { 5 return Ok( 6 new 7 { 8 ID = 1, 9 Name = "consul service", 10 version = 1.0, 11 serviceIP = configuration["ip"], 12 servicePort = configuration["port"] 13 }); 14 }
通过swagger测试返回401
6、swagger中authorize之后返回[Authorize] api controller,返回200
下面介绍下如何在swagger中支持authorize及api分组

1 #region Swagger 2 builder.Services.AddEndpointsApiExplorer(); 3 builder.Services.AddSwaggerGen(c => 4 { 5 //c.IgnoreObsoleteActions(); 6 //c.TagActionsBy(api => 7 //{ 8 // if (api.GroupName != null) 9 // return new[] { api.GroupName }; 10 // var controllerActionDescriptor = api.ActionDescriptor as ControllerActionDescriptor; 11 // if (controllerActionDescriptor != null) 12 // return new[] { controllerActionDescriptor.ControllerName }; 13 // throw new InvalidOperationException("Unable to determine tag for endpoint."); 14 //}); 15 //c.DocInclusionPredicate((name, api) => api.GroupName == "demo1"); 16 17 c.SwaggerDoc("demo1", new OpenApiInfo { Title = "demo1", Description = "this is demo1 v1", Version = "demo1v1" }); 18 c.SwaggerDoc("demo2", new OpenApiInfo { Title = "demo2", Description = "this is demo2 v1", Version = "demo2v1" }); 19 20 c.AddSecurityDefinition("bearerauth", new OpenApiSecurityScheme 21 { 22 Name = "Authorization", 23 Type = SecuritySchemeType.Http, 24 Scheme = "Bearer", 25 BearerFormat = "Bearer", 26 In = ParameterLocation.Header, 27 Description = "JWT Authorization header using the Bearer scheme." 28 }); 29 c.AddSecurityRequirement(new OpenApiSecurityRequirement 30 { 31 { 32 new OpenApiSecurityScheme 33 { 34 //Notes: this is for swagger authentication 35 //Type= SecuritySchemeType.Http, 36 //In= ParameterLocation.Header, 37 38 Reference=new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearerauth" } 39 }, 40 new string[]{} 41 } 42 }); 43 }); 44 #endregion

1 app.UseSwagger(); 2 app.UseSwaggerUI(c => 3 { 4 c.SwaggerEndpoint($"/swagger/demo1/swagger.json", "demo1"); 5 c.SwaggerEndpoint($"/swagger/demo2/swagger.json", "demo2"); 6 });
接下来介绍Authorization Policy及custom policy

1 builder.Services.AddAuthorization(options => 2 { 3 var defaultPolicy = new AuthorizationPolicyBuilder(); 4 defaultPolicy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme); 5 defaultPolicy.RequireAuthenticatedUser(); 6 options.DefaultPolicy = defaultPolicy.Build(); 7 8 options.AddPolicy("CustomScopePolicy", policy => 9 { 10 policy.RequireAuthenticatedUser(); 11 policy.RequireClaim("Scope", "API"); 12 }); 13 options.AddPolicy("CustomRolePolicy", policy => 14 { 15 policy.RequireAuthenticatedUser(); 16 policy.RequireClaim("Role", "Admin"); 17 }); 18 options.AddPolicy("RoleScopePolicy", policy => 19 { 20 policy.RequireAuthenticatedUser(); 21 policy.AddRequirements(new CustomRequirement("admin")); 22 }); 23 }); 24 25 builder.Services.AddTransient<IJwtTokenService, JwtTokenService>(); 26 builder.Services.AddSingleton<IAuthorizationHandler, CustomRoleBaseAuthorizationHandler>(); 27 builder.Services.AddSingleton<IAuthorizationHandler, CustomScopeBaseAuthorizationHandler>();
app.UseAuthorization();

1 public class CustomRoleBaseAuthorizationHandler : AuthorizationHandler<CustomRequirement> 2 { 3 protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement) 4 { 5 var principal = context.User; 6 var roleClaim = principal.Claims.FirstOrDefault(c => c.Type.Equals("Role", StringComparison.OrdinalIgnoreCase)); 7 if (roleClaim?.Value.Equals(requirement.Role, StringComparison.OrdinalIgnoreCase) ?? false) 8 context.Succeed(requirement); 9 return Task.CompletedTask; 10 } 11 } 12 13 public class CustomScopeBaseAuthorizationHandler : AuthorizationHandler<CustomRequirement> 14 { 15 protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement) 16 { 17 var principal = context.User; 18 var claim = principal.Claims.FirstOrDefault(c => c.Type.Equals("Scope", StringComparison.OrdinalIgnoreCase)); 19 if (claim?.Value.Equals("API", StringComparison.OrdinalIgnoreCase) ?? false) 20 context.Succeed(requirement); 21 return Task.CompletedTask; 22 } 23 }

1 public class CustomRequirement : IAuthorizationRequirement 2 { 3 public string Role { get; set; } 4 public CustomRequirement(string role) 5 { 6 Role = role; 7 } 8 }
添加测试controller

1 [HttpGet("v1"), Authorize(Policy = "RoleScopePolicy")] 2 public IActionResult Index() 3 { 4 return new JsonResult(new List<string> { "group1", "group2" }); 5 } 6 7 [HttpGet("v2"),Authorize(Policy = "CustomRolePolicy")] 8 [ApiExplorerSettings(IgnoreApi = false)] 9 public IActionResult IndexV2() 10 { 11 return new JsonResult(new List<string> { "group3", "group4" }); 12 }
测试结果
OK 搞定!