NetCore JWT token

Posted 云霄宇霁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NetCore JWT token相关的知识,希望对你有一定的参考价值。

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

1、create jwt token

 public interface IJwtTokenService
     
         string GetJwtToken();
     
 
     public class JwtTokenService : IJwtTokenService
     
         private readonly IConfiguration _config;
         public JwtTokenService(IConfiguration config)
         
             this._config = config;
         
         public string GetJwtToken()
         
             var claims = new List<Claim>()
             
                 new Claim(JwtRegisteredClaimNames.Sub,"jwtsubvalue"),
                 new Claim(JwtRegisteredClaimNames.Name,"jwtnamevalue"),
                 new Claim("Scope","API"),
                 new Claim("Role","Admin")
             ;
             var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JwtToken:SecretKey"]));
             var credentials = new SigningCredentials(securityKey,SecurityAlgorithms.HmacSha256);
             var issuer = _config["JwtToken:Issuer"];
             var audience = _config["JwtToken:Audience"];
             var expires = DateTime.Now.AddMinutes(Convert.ToDouble(_config["JwtToken:ExpiredMinutes"]));
 
             var jwtToken = new JwtSecurityToken(issuer, audience, claims: claims, notBefore: DateTime.Now, expires: expires, signingCredentials: credentials);
             return new JwtSecurityTokenHandler().WriteToken(jwtToken);
         
     
jwt token

2、configuration中所需配置信息

  "JwtToken": 
     "SecretKey": "12345678901234567890123456789012",
     "Issuer": "https://localhost:5000",
     "Audience": "https://localhost:5000",
     "ExpiredMinutes": 10
   

3、在Program中配置authentication

 var configuration = builder.Configuration;
 builder.Services.AddAuthentication(options =>
 
     options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
     options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
 )
 .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
     
         options.TokenValidationParameters = new TokenValidationParameters
         
             ValidateIssuer = true,
             ValidateAudience = true,
             ValidateLifetime = true,
             ValidateIssuerSigningKey = true,
             ValidIssuer = configuration["JwtToken:Issuer"],
             ValidAudience = configuration["JwtToken:Audience"],
             IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtToken:SecretKey"]))
         ;
         options.Events = new JwtBearerEvents
         
             OnTokenValidated = context =>
             
                 var token = context.SecurityToken as JwtSecurityToken;
                 var identity = context.Principal?.Identity as ClaimsIdentity;
                 if (!string.IsNullOrEmpty(token?.Issuer))
                     context.Success();
                 else
                     context.Fail($"Token invalid");
                 return Task.CompletedTask;
             ,
             OnAuthenticationFailed = context =>
             
                 context.Fail(context.Exception);
                 return Task.CompletedTask;
             ,
             OnForbidden = context =>
             
                 context.Fail("403 forbidden");
                 return Task.CompletedTask;
             ,
             OnChallenge = context =>
             
                 var error = context.Error;
                 return Task.CompletedTask;
             ,
             OnMessageReceived = context =>
             
                 var token = context.Token;
                 return Task.CompletedTask;
             
         ;
         //Configure authentication scheme forwarding to another scheme
         options.ForwardDefaultSelector = context =>
         
             if (context.Request.Headers.TryGetValue("x-api-type", out Microsoft.Extensions.Primitives.StringValues uiTokenType))
                 return "CustomScheme";
             return JwtBearerDefaults.AuthenticationScheme;
         ;
     ).AddJwtBearer("CustomScheme", options =>
     
         options.Authority = configuration["JwtToken:Issuer"];
         options.TokenValidationParameters = new TokenValidationParameters
         
             ValidateIssuer = true,
             ValidIssuer = configuration["JwtToken:Issuer"]
         ;
         options.Events = new JwtBearerEvents
         
             OnTokenValidated = context =>
             
                 context.Success();
                 return Task.CompletedTask;
             
         ;
     );
authentication

4、在请求管道中应用authentication

app.UseAuthentication();

5、添加测试controller,指定[Authorize] atrribute

 [Authorize]
         [HttpGet("all")]
         public IActionResult Get([FromServices] IConfiguration configuration)
         
             return Ok(
                 new
                 
                     ID = 1,
                     Name = "consul service",
                     version = 1.0,
                     serviceIP = configuration["ip"],
                     servicePort = configuration["port"]
                 );
         
test controller

通过swagger测试返回401

 

 6、swagger中authorize之后返回[Authorize] api controller,返回200

 

 

下面介绍下如何在swagger中支持authorize及api分组

 #region Swagger
 builder.Services.AddEndpointsApiExplorer();
 builder.Services.AddSwaggerGen(c => 
 
     //c.IgnoreObsoleteActions();
     //c.TagActionsBy(api =>
     //
     //    if (api.GroupName != null)
     //        return new[]  api.GroupName ;
     //    var controllerActionDescriptor = api.ActionDescriptor as ControllerActionDescriptor;
     //    if (controllerActionDescriptor != null)
     //        return new[]  controllerActionDescriptor.ControllerName ;
     //    throw new InvalidOperationException("Unable to determine tag for endpoint.");
     //);
     //c.DocInclusionPredicate((name, api) => api.GroupName == "demo1");
 
     c.SwaggerDoc("demo1", new OpenApiInfo  Title = "demo1", Description = "this is demo1 v1", Version = "demo1v1" );
     c.SwaggerDoc("demo2", new OpenApiInfo  Title = "demo2", Description = "this is demo2 v1", Version = "demo2v1" );
 
     c.AddSecurityDefinition("bearerauth", new OpenApiSecurityScheme
     
         Name = "Authorization",
         Type = SecuritySchemeType.Http,
         Scheme = "Bearer",
         BearerFormat = "Bearer",
         In = ParameterLocation.Header,
         Description = "JWT Authorization header using the Bearer scheme."
     );
     c.AddSecurityRequirement(new OpenApiSecurityRequirement
     
         
             new OpenApiSecurityScheme
             
                 //Notes: this is for swagger authentication
                 //Type= SecuritySchemeType.Http,
                 //In= ParameterLocation.Header,
 
                 Reference=new OpenApiReference  Type = ReferenceType.SecurityScheme, Id = "bearerauth" 
             ,
             new string[]
         
     );
 );
 #endregion
swagger authorize & group
 app.UseSwagger();
 app.UseSwaggerUI(c =>
 
     c.SwaggerEndpoint($"/swagger/demo1/swagger.json", "demo1");
     c.SwaggerEndpoint($"/swagger/demo2/swagger.json", "demo2");
 );
swagger group endpoint

接下来介绍Authorization Policy及custom policy

 builder.Services.AddAuthorization(options => 
 
     var defaultPolicy = new AuthorizationPolicyBuilder();
     defaultPolicy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
     defaultPolicy.RequireAuthenticatedUser();
     options.DefaultPolicy = defaultPolicy.Build();
 
     options.AddPolicy("CustomScopePolicy", policy => 
     
         policy.RequireAuthenticatedUser();
         policy.RequireClaim("Scope", "API");
     );
     options.AddPolicy("CustomRolePolicy", policy => 
     
         policy.RequireAuthenticatedUser();
         policy.RequireClaim("Role", "Admin");
     );
     options.AddPolicy("RoleScopePolicy", policy =>
     
         policy.RequireAuthenticatedUser();
         policy.AddRequirements(new CustomRequirement("admin"));
     );
 );
 
 builder.Services.AddTransient<IJwtTokenService, JwtTokenService>();
 builder.Services.AddSingleton<IAuthorizationHandler, CustomRoleBaseAuthorizationHandler>();
 builder.Services.AddSingleton<IAuthorizationHandler, CustomScopeBaseAuthorizationHandler>();
policy
app.UseAuthorization();
  public class CustomRoleBaseAuthorizationHandler : AuthorizationHandler<CustomRequirement>
     
         protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement)
         
             var principal = context.User;
             var roleClaim = principal.Claims.FirstOrDefault(c => c.Type.Equals("Role", StringComparison.OrdinalIgnoreCase));
             if (roleClaim?.Value.Equals(requirement.Role, StringComparison.OrdinalIgnoreCase) ?? false)
                 context.Succeed(requirement);
             return Task.CompletedTask;
         
     
 
  public class CustomScopeBaseAuthorizationHandler : AuthorizationHandler<CustomRequirement>
     
         protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement)
         
             var principal = context.User;
             var claim = principal.Claims.FirstOrDefault(c => c.Type.Equals("Scope", StringComparison.OrdinalIgnoreCase));
             if (claim?.Value.Equals("API", StringComparison.OrdinalIgnoreCase) ?? false)
                 context.Succeed(requirement);
             return Task.CompletedTask;
         
     
custom policy handler
  public class CustomRequirement : IAuthorizationRequirement
     
         public string Role  get; set; 
         public CustomRequirement(string role)
         
             Role = role;
            
     
custom requirement

添加测试controller

 [HttpGet("v1"), Authorize(Policy = "RoleScopePolicy")]
         public IActionResult Index()
         
             return new JsonResult(new List<string>  "group1", "group2" );
         
 
         [HttpGet("v2"),Authorize(Policy = "CustomRolePolicy")]
         [ApiExplorerSettings(IgnoreApi = false)]
         public IActionResult IndexV2()
         
             return new JsonResult(new List<string>  "group3", "group4" );
         
test controller

测试结果

 

 OK 搞定!

以上是关于NetCore JWT token的主要内容,如果未能解决你的问题,请参考以下文章

jwt token 过期时间(asp.net core)

从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之七使用JWT生成Token(个人见解)

AuthorizeAttribute with JWT Token- .NET Core 2.0 中的身份验证

Web Token JWT

.netcore 安全(防攻击)

在 ASP.NET Core 中使用基于本地存储的 JWT-Token 更改用户密码(ASP.Identity)