2020年8月1日星期六

asp.net core 3.1 自定义中间件实现jwt token认证

话不多讲,也不知道咋讲!直接上代码

认证信息承载对象【user】

/// <summary>/// 认证用户信息/// </summary>public class DyUser{ /// <summary> /// 用户ID /// </summary> public int UserId { get; set; } /// <summary> /// 所属商户ID /// </summary> public int? TenantId { get; set; }}

Jwt配置对象

public class AuthOptions{ /// <summary> /// Jwt认证Key /// </summary> public string Security { get; set; } /// <summary> /// 过期时间【天】 /// </summary> public int Expiration { get; set; }}

JWT管理接口

public interface IAuthManage{ /// <summary> /// 生成JwtToken /// </summary> /// <param name="user">用户信息</param> /// <returns></returns> string GenerateJwtToken(DyUser user);}

JWT管理接口实现

暂时是使用微软提供类库生成,如果有想法可以自己生成

public class MicrosoftJwtAuthManage : IAuthManage{ private readonly AuthOptions _authOptions; public MicrosoftJwtAuth(AuthOptions authOptions) {  _authOptions = authOptions; } public string GenerateJwtToken(DyUser user) {  var tokenHandler = new JwtSecurityTokenHandler();  var key = Encoding.ASCII.GetBytes(_authOptions.Security);  var tokenDescriptor = new SecurityTokenDescriptor  {   Subject = new ClaimsIdentity(new Claim[]   {    new Claim("user",user.ToJson())   }),   Expires = DateTime.UtcNow.AddDays(_authOptions.Expiration),//一周过期   SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)  };  var token = tokenHandler.CreateToken(tokenDescriptor);  return tokenHandler.WriteToken(token); }}

处理JWT中间件

这里借鉴国外大牛的代码,主要就是验证jwt并且存把解析出来的数据存放到当前上下文

public class JwtMiddleware{ private readonly RequestDelegate _next; private readonly AuthOptions _authOptions; public JwtMiddleware(RequestDelegate next, AuthOptions authOptions) {  _next = next;  _authOptions = authOptions; } public async Task Invoke(HttpContext context) {  //获取上传token,可自定义扩展  var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last()     ?? context.Request.Headers["X-Token"].FirstOrDefault()     ?? context.Request.Query["Token"].FirstOrDefault()     ?? context.Request.Cookies["Token"];  if (token != null)   AttachUserToContext(context, token);  await _next(context); } private void AttachUserToContext(HttpContext context, string token) {  try  {   var tokenHandler = new JwtSecurityTokenHandler();   var key = Encoding.ASCII.GetBytes(_authOptions.Security);   tokenHandler.ValidateToken(token, new TokenValidationParameters   {    ValidateIssuerSigningKey = true,    IssuerSigningKey = new SymmetricSecurityKey(key),    ValidateIssuer = false,    ValidateAudience = false,    // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)    ClockSkew = TimeSpan.Zero   }, out SecurityToken validatedToken);   var jwtToken = (JwtSecurityToken)validatedToken;   var user = jwtToken.Claims.First(x => x.Type == "user").Value.ToJsonEntity<DyUser>();   //写入认证信息,方便业务类使用   var claimsIdentity = new ClaimsIdentity(new Claim[] { new Claim("user", jwtToken.Claims.First(x => x.Type == "user").Value) });   Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentity);   // attach user to context on successful jwt validation   context.Items["User"] = user;  }  catch  {   // do nothing if jwt validation fails   // user is not attached to context so request won't have access to secure routes   throw;  } }}

权限过滤器

这个根据刚才中间件的存放的信息判断是否授权成功,支持匿名特性

public class ApiAuthorizeAttribute : Attribute, IAuthorizationFilter{ public void OnAuthorization(AuthorizationFilterContext context) {  var user = context.HttpContext.Items["User"];  //验证是否需要授权和授权信息  if (HasAllowAnonymous(context) == false && user == null)  {   // not logged in   context.Result = new JsonResult(new {message = "Unauthorized"})    {StatusCode = StatusCodes.Status401Unauthorized};  } } private static bool HasAllowAnonymous(AuthorizationFilterContext context) {  var filters = context.Filters;  if (filters.OfType<IAllowAnonymousFilter>().Any())  {   return true;  }  // When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that  // were discovered on controllers and actions. To maintain compat with 2.x,  // we'll check for the presence of IAllowAnonymous in endpoint metadata.  var endpoint = context.HttpContext.GetEndpoint();  return endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null; }}

扩展IServiceCollection

方便以后管理和维护,主要就是把需要的对象注入到IOC容器里面

public static class AuthServiceExtensions{ public static void AddAuth(this IServiceCollection services, Action<AuthOptions> configAction) {  var options = new AuthOptions();  configAction(options);  services.AddSingleton(options);  services.AddSingleton<IAuthManage>(new MicrosoftJwtAuthManage(options)); }}

NullDySession

这里是为了在非控制器类获取用户信息用

/// <summary>/// 当前会话对象/// </summary>public class NullDySession{ /// <summary> /// 获取DySession实例 /// </summary> public static NullDySession Instance { get; } = new NullDySession(); /// <summary> /// 获取当前用户信息 /// </summary> public DyUser DyUser {  get  {   var claimsPrincipal = Thread.CurrentPrincipal as ClaimsPrincipal;   var claimsIdentity = claimsPrincipal?.Identity as ClaimsIdentity;   var userClaim = claimsIdentity?.Claims.FirstOrDefault(c => c.Type == "user");   if (userClaim == null || string.IsNullOrEmpty(userClaim.Value))   {    return null;   }   return userClaim.Value.ToJsonEntity<DyUser>();  } } private NullDySession() { }}

到这为止准备工作完成,开始用起来吧~

修改【Startup.cs->ConfigureServices】

//添加全局权限认证过滤器services.AddControllersWithViews(options =>{	options.Filters.Add<ApiAuthorizeAttribute>();})//添加认证配置信息services.AddAuth(options =>{	options.Expiration = 7;//天为单位	options.Security = apolloConfig.Get("JwtSecret");});

添加中间件【Startup.cs->Configure(IApplicationBuilder app, IWebHostEnvironment env)方法中】

注意中间件的位置

//启用jwt认证中间件app.UseMiddleware<JwtMiddleware>();

api使用案例【使用构造注入IAuthManage】

//生成了JwtTokenvar newToken = _authManage.CreateJwtToken(para.Sn);//Controller里面获取用户信息public DyUser DyUser => (DyUser)this.HttpContext.Items["User"];//普通class类获取用户信息【如果不是Web应用,需要独立引用Dymg.Core】NullDySession.Instance.DyUser.UserId;//如果个别不接口不需要认证,可以使用AllowAnonymous特性[HttpPost, AllowAnonymous]public string Noauth(){	return "这个不需要授权";}

前端调用案例

//token放在请求头里面Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y//自定义keyx-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y//使用连接字符串方式https://xxxxx/user/getUser?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y
asp.net core 3.1 自定义中间件实现jwt token认证八招教你如何把优化做到极致米兰网2016 CSS公版系统发布会做无货源铺货模式,为什么会选择亚马逊平台?Tinypng汇通达2017年深圳返程票可提前几天买?2017年春运深圳长途汽车票提前几天订票?怎样买汽车票?2017年春节深圳世界之窗有什么活动?好玩吗?

没有评论:

发表评论