- 在appsettings.json中配置JWT参数
"JWTSetting": { "Issuer": "", //颁发者 "audience": "jwtAudience", //可以给那些客户端使用 "SecretKey": "8kh2luzmp0oq9wfbdeasygj647vr531n678fs" //加密Key 必须大于16个字符 }
- 在TEST.Entity中创建令牌实体
/// <summary> /// 令牌 /// </summary> public class TokenData { /// <summary> /// ID /// </summary> public string Uid { get; set; } /// <summary> /// 角色 /// </summary> public string Role { get; set; } }
- 新增JWTHelper.cs帮助类
/// <summary> /// JWT帮助类 /// </summary> public class JWTHelper { /// <summary> /// 颁发JWT字符串 /// </summary> /// <param name="tokenData"></param> /// <returns></returns> public static string IssueJwt(TokenData tokenData) { //获取appsetting配置 var iss = AppSettings.App(new string[] { "JWTSetting", "Issuer" }); var aud = AppSettings.App(new string[] { "JWTSetting", "audience" }); var secret = AppSettings.App(new string[] { "JWTSetting", "SecretKey" }); var claims = new List<Claim> { /* * 特别重要: * 1.这里将用户的部分信息,比如UID 存到了Claim中,如果你想知道如何在其他地方将这个UID从Token中取出来, * 请看下面的SerializeJwt()方法,或者在整个解决方案,搜索这个方法,看看哪里使用了! * 2.你也可以研究下 HttpContext.User.Claims,具体的你可以看看 Policys/PermissionHanlder.cs类中是如何使用的。 */ new Claim(JwtRegisteredClaimNames.Jti,tokenData.Uid.ToString()), new Claim(JwtRegisteredClaimNames.Iat,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"), new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"), //这个就是过期时间,目前是过期1000秒,可自定义,注意JWT有自己的缓冲过期时间 new Claim(JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds()}"), new Claim(ClaimTypes.Expiration,DateTime.Now.AddSeconds(1000).ToString()), new Claim(JwtRegisteredClaimNames.Iss,iss), new Claim(JwtRegisteredClaimNames.Aud,aud), }; //可以将一个用户的多个角色全部赋予 claims.AddRange(tokenData.Role.Split(',') .Select(s => new Claim(ClaimTypes.Role, s))); //秘钥(SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常) var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var jwt = new JwtSecurityToken( issuer: iss, claims: claims, signingCredentials: creds ); var jwtHandler = new JwtSecurityTokenHandler(); var encodedJwt = jwtHandler.WriteToken(jwt); return encodedJwt; } /// <summary> /// 解析 /// </summary> /// <param name="jwtStr"></param> /// <returns></returns> public static TokenData SerializeJwt(string jwtStr) { var jwtHandler = new JwtSecurityTokenHandler(); JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr); object role = ""; try { jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role); } catch (Exception ex) { Console.WriteLine(ex); } var td = new TokenData { Uid = jwtToken.Id.ToString(), Role = role != null ? role.ToString() : "", }; return td; } }
- 在TestController中新增Login接口来获取Token
/// <summary> /// 获取Token /// </summary> /// <param name="name"></param> /// <param name="password"></param> /// <returns></returns> [HttpGet] [AllowAnonymous] public async Task<IActionResult> Login(string name, string password) { var jwtStr = string.Empty; bool suc = false; if (name != null && password != null) { if (name != "admin" && password != "123456") { jwtStr = "输入账号密码错误!"; } else { //将用户和角色作为单独的自定义变量封装进token字符串中。 var tokenData = new TokenData { Uid = "1", Role = name }; jwtStr = JWTHelper.IssueJwt(tokenData);//登录,获取到一定规则的Token令牌 suc = true; } } else { jwtStr = "输入不能为空!"; } return Ok(new { success = suc, token = jwtStr }); }
- 运行项目,输入账号密码获取Token
- JWT权限验证,就需要开启验证,然后输入Token令牌,在SwaggerHelper.cs的AddSwaggerSetup方法中添加代码:
/// <summary> /// Swagger /// </summary> /// <param name="services"></param> public static void AddSwaggerSetup(this IServiceCollection services) { if(services == null) throw new ArgumentNullException(nameof(services)); var ApiName = "TEST API"; services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = $"{ApiName} 接口文档— TEST API", Description = $"{ApiName} HTTPS TEST API v1" }); options.OrderActionsBy(o => o.RelativePath); var xmlPath = Path.Combine(AppContext.BaseDirectory, "TEST.API.xml"); //这个就是刚刚设置的xml文件名 options.IncludeXmlComments(xmlPath,false); var xmlEntityPath = Path.Combine(AppContext.BaseDirectory, "TEST.Entity.xml");//这个就是Model的xml文件名 options.IncludeXmlComments(xmlEntityPath); //在Header中添加token,传递到后台 options.OperationFilter<SecurityRequirementsOperationFilter>(); #region Token绑定到ConfigureServices options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JT授权(数据将在请求头中进行传输)直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"", Name = "Authorization",//jwt默认的参数名称 In = ParameterLocation.Header, //jwt默认存放Authorization信息的位置(请求头) Type = SecuritySchemeType.ApiKey }); #endregion }); }
- 运行项目就可以看到JWT验证入口
-
在Business文件夹中新建注册方法AuthorizationSetup
public static class AuthorizationSetup { public static void AddAuthorizationSetup(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); //1.授权 这个和上面的异曲同工,好处就是不用再Controller中,写多个 roles. // [Authorize(Policy = "Admin")] services.AddAuthorization(option => { option.AddPolicy("User", policy => policy.RequireRole("User").Build()); option.AddPolicy("System", policy => policy.RequireRole("System").Build()); option.AddPolicy("SystemOrAdmin", policy => policy.RequireRole("admin").Build()); }); //读取配置文件 var symmetricKeyAsBase64 = AppSettings.App(new string[] { "JWTSetting", "SecretKey" }); var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey = new SymmetricSecurityKey(keyByteArray); var Issuer = AppSettings.App(new string[] { "JWTSetting", "Issuer" }); var Audience = AppSettings.App(new string[] { "JWTSetting", "Audience" }); //令牌验证参数 var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, ValidateIssuer = true, ValidIssuer = Issuer, //发行人 ValidateAudience = true, ValidAudience = Audience, //订阅人 ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(30), RequireExpirationTime = true, }; //2.认证 core自带官方JWT认证 //开启Bearer认证 services.AddAuthentication("Bearer") //添加JwtBearer服务 .AddJwtBearer(o => { o.TokenValidationParameters = tokenValidationParameters; o.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents { OnAuthenticationFailed = context => { //如果过期,则把<是否过期>添加到返回头信息中 if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Add("Token-Expired", "true"); } return Task.CompletedTask; } }; }); } }
- 在Program.cs注册服务,开启服务
//jwt授权认证 builder.Services.AddAuthorizationSetup();
app.UseAuthentication();
- 在BaseController中设置全局权限验证:
[Route("api/[controller]/[action]")] [ApiController] [Authorize] public class BaseController : ControllerBase { }
- 没有权限验证就会提示401
- 设置获取Token 时[AllowAnonymous] 时无需验证
- 设置测试方法需要System权限才能访问
- 上面我们使用admin访问就会提示403错误
- 新增解析Token方法
[HttpGet] [Authorize] public IActionResult ParseToken() { //需要截取Bearer var tokenHeader = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); var user = JWTHelper.SerializeJwt(tokenHeader); return Ok(user); }
- 解析出来的role 就是admin
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容