JWT
1、解析
1)客户端向授权服务系统发起请求,申请获取“令牌”。
2)授权服务根据用户身份,生成一张专属“令牌”,并将该“令牌”以JWT规范返回给客户端
3)客户端将获取到的“令牌”放到http请求的headers中后,向主服务系统发起请求。主服务系统收到请求后会从headers中获取“令牌”,并从“令牌”中解析出该用户的身份权限,然后做出相应的处理(同意或拒绝返回资源)
2、配置JWT
1、添加NuGet包Microsoft.AspNetCore.Authentication.JwtBearer
2、在appsettings.json
中添加JWT配置节点
"JWT": { "SecKey": "Jamin1127!#@$%@%^^&*(~Czmjklneafguvioszb%yuv&*6WVDf5dw#5dfw6f5w6faW%FW^f5wa65f^AWf56", //密钥 "Issuer": "Jamin", //发行者 "ExpireSeconds": 7200 //过期时间 }
3、在Program类里进行服务注册
#region JWT服务 // 注册JWT服务 builder.Services.AddSingleton(new JwtHelper(builder.Configuration)); builder.Services.AddAuthentication( JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true, //是否验证Issuer ValidIssuer = builder.Configuration["Jwt:Issuer"], //发行人Issuer ValidateAudience = false, //是否验证Audience ValidateIssuerSigningKey = true, //是否验证SecurityKey IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecKey"])), //SecurityKey ValidateLifetime = true, //是否验证失效时间 ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒) RequireExpirationTime = true, }; } ); #endregion //swagger里添加JWT授权 builder.Services.AddSwaggerGen(c=> { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Web API", Version = "v1" }); //开启注释 var xmlFile = $"{Assembly.GetEntryAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath, true); // 配置 JWT Bearer 授权 c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Description = "JWT Authorization header using the Bearer scheme", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.Http, Scheme = "bearer" }); var securityScheme = new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }; var securityRequirement = new OpenApiSecurityRequirement { { securityScheme, new string[] { } } }; c.AddSecurityRequirement(securityRequirement); }); //启用验证中间件 app.UseAuthentication(); app.UseAuthorization();
4、创建JWT类进行Token配置
using Microsoft.IdentityModel.Tokens; using System.Diagnostics; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; namespace Blog.core.Common.Auth { /// <summary> /// 授权JWT类 /// </summary> public class JwtHelper { private readonly IConfiguration _configuration; /// <summary> /// Token配置 /// </summary> /// <param name="configuration"></param> public JwtHelper(IConfiguration configuration) { _configuration = configuration; } /// <summary> /// 创建Token 这里面可以保存自己想要的信息 /// </summary> /// <param name="username"></param> /// <param name="mobile"></param> /// <returns></returns> public string CreateToken(string username, string mobile) { try { // 1. 定义需要使用到的Claims var claims = new[] { new Claim("username", username), new Claim("mobile", mobile), /* 可以保存自己想要信息,传参进来即可 new Claim("sex", "sex"), new Claim("limit", "limit"), new Claim("head_url", "xxxxx") */ }; // 2. 从 appsettings.json 中读取SecretKey var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecKey"])); // 3. 选择加密算法 var algorithm = SecurityAlgorithms.HmacSha256; // 4. 生成Credentials var signingCredentials = new SigningCredentials(secretKey, algorithm); // 5. 根据以上,生成token var jwtSecurityToken = new JwtSecurityToken( _configuration["Jwt:Issuer"], //Issuer _configuration["Jwt:ExpireSeconds"], //ExpireSeconds claims, //Claims, DateTime.Now, //notBefore DateTime.Now.AddSeconds(30), //expires signingCredentials //Credentials ); // 6. 将token变为string var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); return token; } catch (Exception) { throw; } } /// <summary> /// 获取信息 /// </summary> /// <param name="jwt"></param> /// <returns></returns> public static string ReaderToken(string jwt) { var str = string.Empty; try { //获取Token的三种方式 //第一种直接用JwtSecurityTokenHandler提供的read方法 var jwtHander = new JwtSecurityTokenHandler(); JwtSecurityToken jwtSecurityToken = jwtHander.ReadJwtToken(jwt); str = jwtSecurityToken.ToString(); } catch (Exception ex) { Debug.WriteLine(ex.Message); } return str; } /// <summary> /// 解密jwt /// </summary> /// <param name="jwt"></param> /// <returns></returns> public string JwtDecrypt(string jwt) { StringBuilder sb = new StringBuilder(); try { JwtSecurityTokenHandler tokenHandler = new(); TokenValidationParameters valParam = new(); var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecKey"])); valParam.IssuerSigningKey = securityKey; valParam.ValidateIssuer = false; valParam.ValidateAudience = false; //解密 ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt, valParam, out SecurityToken secToken); foreach (var claim in claimsPrincipal.Claims) { sb.Append($"{claim.Type}={claim.Value}"); } } catch (Exception ex) { Debug.WriteLine(ex.Message); } return sb.ToString(); } } }
5、创建用户实体,进行用户密码的接收
using System.ComponentModel.DataAnnotations; namespace Blog.core.Models { public class UserInfo { /// <summary> /// 其中 [Required] 表示非空判断,其他自己研究百度 /// </summary> [Required] public string UserName { get; set; } [Required] public string Password { get; set; } [Required] public string PhoneNumber { get; set; } } }
6、创建控制器,进行JWT的APi调用
using Blog.core.Common.Auth; using Blog.core.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Blog.core.Controllers { [Route("[controller]/[action]")] [ApiController] public class UserController : ControllerBase { private readonly JwtHelper _jwt; /// <summary> /// 初始化 /// </summary> /// <param name="jwtHelper"></param> public UserController(JwtHelper jwtHelper) { _jwt = jwtHelper; } /// <summary> /// 获取Token /// </summary> /// <returns></returns> [HttpPost] public IActionResult GetToken(UserInfo user) { //参数验证等等.... if (string.IsNullOrEmpty(user.UserName)) { return Ok("参数异常!"); } //这里可以连接mysql数据库做账号密码验证 //这里可以做Redis缓存验证等等 //这里获取Token,当然,这里也可以选择传结构体过去 var token = _jwt.CreateToken(user.UserName, user.PhoneNumber); //解密后的Token var PWToken = _jwt.JwtDecrypt( token); return Ok(token+"解密后:"+PWToken); } /// <summary> /// 获取自己的详细信息,其中 [Authorize] 就表示要带Token才行 /// </summary> /// <returns></returns> [HttpPost] [Authorize] public IActionResult GetSelfInfo() { //执行到这里,就表示已经验证授权通过了 /* * 这里返回个人信息有两种方式 * 第一种:从Header中的Token信息反向解析出用户账号,再从数据库中查找返回 * 第二种:从Header中的Token信息反向解析出用户账号信息直接返回,当然,在前面创建 Token时,要保存进使用到的Claims中。 */ return Ok("授权通过了!"); } } }
注:获取Token后在Swagger上输入token的value就可以进行接口的调用了