.Net Core Jwt验证
1.准备
新建一个.Net Core项目,添加Jwt控制器
2.JwtController
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; using System; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; namespace StudyToken2.Controllers { [Route("api/Token")] public class JwtController : Controller { public IActionResult Index() { return View(); } [HttpPost] public IActionResult RequestToken([FromBody] TokenRequest request) { string SecurityKey = "MySecurityKey!!kxy"; if (request.Username == "AngelaDaddy" && request.Password == "123456") { // push the user’s name into a claim, so we can identify the user later on. var claims = new[] { new Claim(ClaimTypes.Name, request.Username),//账号 new Claim(ClaimTypes.Role, request.Role),//角色 }; //sign the token using a secret key.This secret will be shared between your API and anything that needs to check that the token is legit. var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); //.NET Core’s JwtSecurityToken class takes on the heavy lifting and actually creates the token. /** * Claims (Payload) Claims 部分包含了一些跟这个 token 有关的重要信息。 JWT 标准规定了一些字段,下面节选一些字段: iss: The issuer of the token,token 是给谁的 sub: The subject of the token,token 主题 exp: Expiration Time。 token 过期时间,Unix 时间戳格式 iat: Issued At。 token 创建时间, Unix 时间戳格式 jti: JWT ID。针对当前 token 的唯一标识 除了规定的字段外,可以包含其他任何 JSON 兼容的字段。 * */ var token = new JwtSecurityToken( issuer: "yourdomain.com", audience: "yourdomain.com", claims: claims, expires: DateTime.Now.AddMinutes(30), signingCredentials: creds); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) }); } return BadRequest("Could not verify username and password"); } } public class TokenRequest { public string Username { get; set; } public string Password { get; set; } public string Role { get; set; } } }
3.配置文件
ConfigureServices
//授权 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.RequireHttpsMetadata = false;//是否需要https options.SaveToken = true;//是否将信息存储到token options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false,//是否验证Issuer ValidateAudience = false,//是否验证Audience ValidateLifetime = true,//是否验证失效时间 ValidateIssuerSigningKey = true,//是否验证SecurityKey ValidAudience = "http://localhost:5000",//Audience ValidIssuer = "http://localhost:5000",//Issuer,这两项和前面签发jwt的设置一致 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("VertivSecurityKey001")),//拿到SecurityKey }; } );
Configure
//Token 认证和授权,顺序不能乱 app.UseAuthentication(); app.UseAuthorization(); app.UseStatusCodePages(new StatusCodePagesOptions() { HandleAsync = (context) => { if (context.HttpContext.Response.StatusCode == 401) { using (System.IO.StreamWriter sw = new System.IO.StreamWriter(context.HttpContext.Response.Body)) { sw.Write(Newtonsoft.Json.JsonConvert.SerializeObject(new { status = 401, message = "未授权", })); } } if (context.HttpContext.Response.StatusCode == 403) { using (System.IO.StreamWriter sw = new System.IO.StreamWriter(context.HttpContext.Response.Body)) { sw.Write(Newtonsoft.Json.JsonConvert.SerializeObject(new { status = 403, message = "Role不匹配", })); } } return Task.Delay(0); } });
4.接口测试
在Home中添加接口
[HttpGet("ceshi2")] [Authorize(Roles = "mall,tenant")]//可以不指定角色 public string ceshi2() { return "访问成功!"; }
为了方便测试,我再控制器上加上了路由规则
[Route("api/[controller]")]
[AllowAnonymous] 可以跳过验证
5.postman测试
1)获取Token
https://localhost:44346/api/Token
raw参数
{ "Username":"AngelaDaddy", "Password":"123456", "Role":"mall" }
生成结果
{ "token": "mytoken" }
2)使用token访问接口ceshi2
https://localhost:44346/api/home/ceshi2
Header
Authorization:Bearer mytoken
6.附上控制器扩展函数
public static class ControllerExt { /// <summary> /// 是商管账号登录了 /// </summary> /// <param name="controller"></param> /// <returns></returns> public static bool IsMall(this Controller controller) => controller.User?.Identity != null && controller.User.Claims.Any(c => c.Type == ClaimTypes.Role && c.Value == "mall"); /// <summary> /// 是租户账号登录了 /// </summary> /// <param name="controller"></param> /// <returns></returns> public static bool IsTenant(this Controller controller) => controller.User?.Identity != null && controller.User.Claims.Any(c => c.Type == ClaimTypes.Role && c.Value == "tenant"); /// <summary> /// 租户账号体系下的账号ID /// </summary> /// <param name="controller"></param> /// <returns></returns> public static string TenantAccountCode(this Controller controller) => controller.IsTenant() ? controller.User.Identity.Name : null; /// <summary> /// 商场账号体系下的账号 /// </summary> /// <param name="controller"></param> /// <returns></returns> public static string MallAccountCode(this Controller controller) => controller.IsMall() ? controller.User.Identity.Name : null; }
7.将StartUp的配置抽离出来
1)ConfigureServices 部分
新建一个类:
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace JwtHelperCore {//扩展方法 public static class JwtServiceExtensions { public static AuthenticationBuilder AddJwtService(this IServiceCollection services) { //lambda跑了异步,数据加载不到,直接写函数体,GetJwtOptions作废 //return services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // .AddJwtBearer(options => JwtService.GetJwtOptions()); return services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.RequireHttpsMetadata = false;//是否需要https options.SaveToken = true;//是否将信息存储到token options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false,//是否验证Issuer ValidateAudience = false,//是否验证Audience ValidateLifetime = true,//是否验证失效时间 ValidateIssuerSigningKey = true,//是否验证SecurityKey ValidAudience = "http://localhost:5000",//Audience ValidIssuer = "http://localhost:5000",//Issuer,这两项和前面签发jwt的设置一致 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("VertivSecurityKey001")),//拿到SecurityKey }; } ); } } }
在ConfigureServices 添加
//Token 授权 services.AddJwtService();
2)Configure 部分
新建一个类:
using ComLibrary; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using System; using System.Threading.Tasks; namespace JwtHelperCore { public class ErrorHandlingMiddleware { private readonly RequestDelegate next; public ErrorHandlingMiddleware(RequestDelegate next) { this.next = next; } //先跑这里 public async Task Invoke(HttpContext context) { try { await next(context); } catch (Exception ex) { var statusCode = context.Response.StatusCode; if (ex is ArgumentException) { statusCode = 200; } await HandleExceptionAsync(context, statusCode, ex.Message); } finally { var statusCode = context.Response.StatusCode; var msg = ""; if (statusCode == 401) { msg = "未授权"; } else if (statusCode == 403) { msg = "Role错误"; } else if (statusCode == 404) { msg = "未找到服务"; } else if (statusCode == 502) { msg = "请求错误"; } else if (statusCode != 200) { msg = "未知错误"; } if (!string.IsNullOrWhiteSpace(msg)) { await HandleExceptionAsync(context, statusCode, msg); } } } //异常错误信息捕获,将错误信息用Json方式返回 private static Task HandleExceptionAsync(HttpContext context, int statusCode, string msg) { var result = JsonConvert.SerializeObject(new ResultDataModel<string>() { Success = false, Message = msg, ErrCode = statusCode }); context.Response.ContentType = "application/json;charset=utf-8"; return context.Response.WriteAsync(result); } } //扩展方法 public static class ErrorHandlingExtensions { public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder) { return builder.UseMiddleware<ErrorHandlingMiddleware>(); } } }
在 Configure 添加
//Token 异常捕捉、授权、认证 app.UseErrorHandling(); app.UseAuthentication(); app.UseAuthorization();
8.token输入框不显示问题
不知怎么了,token输入框忽然消失了,通过以下配置可以使用同一的token
ConfigureServices
//Swagger services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "VPart.xml")); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme() { Description = "Bearer +{token}", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.ApiKey, }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference() { Id = "Bearer", Type = ReferenceType.SecurityScheme } }, Array.Empty<string>() } }); });
效果为:


浙公网安备 33010602011771号