asp net core2.1如何使用jwt从原理到精通(二)

在aspnet core中,自定义jwt管道验证

有了上一节的内容作为基础,那这点也是非常容易的,关键点在中间件,只是把上一级在测试类中的自定义验证放到中间件中来即可,

不过需要注意:中间件 的位置很重要,只有它后面的管道才会收到影响;

那我们先建一个自定义中间件类:(中间件的详细内容这里就不讲了,大家可以参考官网和其他博文)

/// <summary>
 /// 自定义授权中间件
 /// </summary>
 public class JwtCustomerAuthorizeMiddleware
 {
  private readonly RequestDelegate next;

  public JwtCustomerAuthorizeMiddleware(RequestDelegate next, string secret, List<string> anonymousPathList)
  {
   #region 设置自定义jwt 的秘钥
   if(!string.IsNullOrEmpty(secret))
   {
    TokenContext.securityKey = secret;
   }
   #endregion
   this.next = next;
   UserContext.AllowAnonymousPathList.AddRange(anonymousPathList);
  }

  public async Task Invoke(HttpContext context, UserContext userContext,IOptions<JwtOption> optionContainer)
  {
   if (userContext.IsAllowAnonymous(context.Request.Path))
   {
    await next(context);
    return;
   }

   var option = optionContainer.Value;

   #region 身份验证,并设置用户Ruser值

   var result = context.Request.Headers.TryGetValue("Authorization", out StringValues authStr);
   if (!result || string.IsNullOrEmpty(authStr.ToString()))
   {
    throw new UnauthorizedAccessException("未授权");
   }
   result = TokenContext.Validate(authStr.ToString().Substring("Bearer ".Length).Trim(), payLoad =>
   {
    var success = true;
    //可以添加一些自定义验证,用法参照测试用例
    //验证是否包含aud 并等于 roberAudience
    success = success && payLoad["aud"]?.ToString() == option.Audience;
    if (success)
    {
     //设置Ruse值,把user信息放在payLoad中,(在获取jwt的时候把当前用户存放在payLoad的ruser键中)
     //如果用户信息比较多,建议放在缓存中,payLoad中存放缓存的Key值
     userContext.TryInit(payLoad["ruser"]?.ToString());
    }
    return success;
   });
   if (!result)
   {
    throw new UnauthorizedAccessException("未授权");
   }

   #endregion
   #region 权限验证
   if (!userContext.Authorize(context.Request.Path))
   {
    throw new UnauthorizedAccessException("未授权");
   }
   #endregion

   await next(context);
  }
 }

上面这个中间件中有个UserContext上线文,这个类主要管理当前用户信息和权限,其他信息暂时不管;我们先看一下这个中间件的验证流程如何:

该中间件主要是针对访问的路径进行验证,当然你也可以针对其他信息进行验证,比如(控制器名称,动作名称,等)

  • 检查当前url是否可以匿名访问,如果可以就直接通过,不做验证了;如果不是可以匿名访问的路径,那就继续
  • 获取当前http头部携带的jwt(存放在头部的 Authorization中);
  • 使用上一节的讲的TokenContext做必须的验证和自定义复杂验证;
  • 获取当前访问用户信息,我们把用户的基本信息放在payLoad["ruser"]中,请看代码如何操作
  • 到这里为止,都是做的身份验证,表明你是一个有身份的的人;接下来是做权限验证,你是一个有身份的人,并不代表你是一个随便到处访问的人;你能访问哪些url或者action,就要得到权限验证的认可
  • 我们把权限验证放到 userContext.Authorize方法中(这里怎么操作,这里就不深入讲解,基本原理是从数据库或者缓存中获取当前用户对应的权限列表,也就是url列表,进行对比);

自定义中间件使用jwt验证就这些内容,是不是感觉很清晰,很简单,有木有;

中间已经完成了,那接下来我们来使用它,我们再startup中的Configure方法中添加如下代码

app.UseMiddleware<JwtCustomerAuthorizeMiddleware>(Configuration["JwtOption:SecurityKey"], new List<string>() { "/api/values/getjwt","/" });

当然上面可匿名访问的url也可以定义在appsetting.json文件中,可以自行尝试

如何通过自定义策略形式实现自定义jwt验证

创建自定义策略的详细介绍可以参考官网,这里就不详细介绍,

首先我们上代码,创建自定义策略非常重要的两个类,如下:

public class CommonAuthorizeHandler : AuthorizationHandler<CommonAuthorize>
 {
  /// <summary>
  /// 常用自定义验证策略,模仿自定义中间件JwtCustomerauthorizeMiddleware的验证范围
  /// </summary>
  /// <param name="context"></param>
  /// <param name="requirement"></param>
  /// <returns></returns>
  protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CommonAuthorize requirement)
  {
   var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext;
   var userContext = httpContext.RequestServices.GetService(typeof(UserContext)) as UserContext;

   var jwtOption = (httpContext.RequestServices.GetService(typeof(IOptions<JwtOption>)) as IOptions<JwtOption>).Value;

   #region 身份验证,并设置用户Ruser值

   var result = httpContext.Request.Headers.TryGetValue("Authorization", out StringValues authStr);
   if (!result || string.IsNullOrEmpty(authStr.ToString()))
   {
    return Task.CompletedTask;
   }
   result = TokenContext.Validate(authStr.ToString().Substring("Bearer ".Length).Trim(), payLoad =>
   {
    var success = true;
    //可以添加一些自定义验证,用法参照测试用例
    //验证是否包含aud 并等于 roberAudience
    success = success && payLoad["aud"]?.ToString() == jwtOption.Audience;
    if (success)
    {
     //设置Ruse值,把user信息放在payLoad中,(在获取jwt的时候把当前用户存放在payLoad的ruser键中)
     //如果用户信息比较多,建议放在缓存中,payLoad中存放缓存的Key值
     userContext.TryInit(payLoad["ruser"]?.ToString());
    }
    return success;
   });
   if (!result)
   {
    return Task.CompletedTask;
   }

   #endregion
   #region 权限验证
   if (!userContext.Authorize(httpContext.Request.Path))
   {
    return Task.CompletedTask;
   }
   #endregion

   context.Succeed(requirement);
   return Task.CompletedTask;
  }
 }
 public class CommonAuthorize: IAuthorizationRequirement
 {

 }

其中两个重要的类是哪两个呢?他们的作用又是什么呢?

1、CommonAuthorize: IAuthorizationRequirement,至于取什么名字,自己定义,但必须继承IAuthorizationRequirement,这类主要是承载一些初始化值,让后传递到Handler中去,给验证做逻辑运算提供一些可靠的信息;我这里是空的;自己根据自身情况自己定义适当的属性作为初始数据的承载容器;

2、CommonAuthorizeHandler : AuthorizationHandler<CommonAuthorize>这个是重点,承载了验证的逻辑运算
需要重写override Task HandleRequirementAsync方法,所有的逻辑都在该方法中,他的主要逻辑和上面的自定义中间件很相似,只少了上面的第一步;验证流程如下:

  • 获取当前http头部携带的jwt(存放在头部的 Authorization中);
  • 使用上一节的讲的TokenContext做必须的验证和自定义复杂验证;
  • 获取当前访问用户信息,我们把用户的基本信息放在payLoad["ruser"中,请看代码如何操作
  • 到这里为止,都是做的身份验证,表明你是一个有身份的的人;接下来是做权限验证,你是一个有身份的人,并不代表你是一个随便到处访问的人;你能访问哪些url或者action,就要得到权限验证的认可
  • 我们把权限验证放到 userContext.Authorize方法中(这里怎么操作,这里就不深入讲解,基本原理是从数据库或者缓存中获取当前用户对应的权限列表,也就是url列表,进行对比);
    context.Succeed(requirement);是验证成功,如果没有这个,就默认验证失败
    因为UserContext把负责了权限验证,所以不会把流程搞得感觉很乱,并且可以重用,至于用那种形式验证也很容易切换

3、是不是很简单,和自定义管道验证的的代码几乎一模一样,

如何使用自定义定义策略呢?

1、在startup类中的ConfigureServices中加入如下代码:

services.AddAuthorization(option =>
   {

    #region 自定义验证策略
    option.AddPolicy("common", policy => policy.Requirements.Add(new CommonAuthorize()));
    #endregion

   }).AddAuthentication(option =>
   {
    option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
   }).AddJwtBearer(option =>
   {
    if (!string.IsNullOrEmpty(config["JwtOption:SecurityKey"]))
    {
     TokenContext.securityKey = config["JwtOption:SecurityKey"];
    }
    //设置需要验证的项目
    option.TokenValidationParameters = new TokenValidationParameters
    {

     IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(TokenContext.securityKey))//拿到SecurityKey
    };
   });

   //自定义策略IOC添加

   services.AddSingleton<IAuthorizationHandler, CommonAuthorizeHandler>();

以上代码主要分3个部分

1、添加上面自定义的策略,并取名;

2、设置秘钥,这个秘钥就是上一节中生成jwt的秘钥,必须要要一样,否则是签名不正确

3、注入上面建立的一个重要类CommonAuthorizeHandler,如上面代码

2、在startup类中的Configure中添加 app.UseAuthentication();

3、在需要验证的Controller或者Action中加上[Authorize(Policy = "common")]属性,看下图:

到此为止你就可以使用自定义策略的验证了;

使用管道和自定义策略两种形式进行验证有什么区别呢?

从效果上看都是一样的,稍微有点区别

  • 使用管道的方式,感觉方便点,清晰点
  • 使用自定义策略的方式,效率稍微高一点,毕竟不是所有的请求都会进行是否可以匿名访问运算和建立管道的消耗,只有加入Authorize属性的Controller和Action的才会进入;当然这点损耗可以忽略不计,看自己的喜好;

至于你喜欢那种,就使用哪种吧,性能可以忽略不计;

不管使用哪种方式使用jwt作为身份和权限验证是不是很简单,关键这里也把权限验证的逻辑抽出来了,这样代码就更清晰明了了;

至于Authorize的属性形式,还有很多其他的策略,比如用户、申明,角色等,可查看官网https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/?view=aspnetcore-2.0

下一章将讲解 用户,申明 ,角色的验证,并这些怎么在自定义的验证中实现,以便大家对他有个清晰的对比

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Asp.Net Core基于JWT认证的数据接口网关实例代码

    前言 近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对前后端分离的数据服务支持,于是想到我一直做.Net开发,问我是否对.Net Core有所了解?能不能做个简单Demo出来看看?我说,分道扬镳之后我不是调用别人的接口就是提供接口给别人调用,于是便有了以下示例代码. 示例要求能演示获取Token及如何使用该Token访问数据资源,在Demo中实现

  • asp net core 2.1中如何使用jwt(从原理到精通)

    为什么使用 Jwt 最近,移动开发的劲头越来越足,学校搞的各种比赛都需要用手机 APP 来撑场面,所以,作为写后端的,很有必要改进一下以往的基于 Session 的身份认证方式了,理由如下: 移动端经常要保持长时间(1 到 2 星期)在线,但是 Session 却不好在服务端保存这么久,虽然可以持久化到数据库,但是还是挺费资源 移动端往往不是使用的网页技术,所以藏在 Cookie 里面的 SessionId 不是很方便的传递给服务端 服务端暴露给客户端的接口往往是 RESTful 风格的,这是一

  • .net core webapi jwt 更为清爽的认证详解

    我的方式非主流,控制却可以更加灵活,喜欢的朋友,不妨花一点时间学习一下 jwt认证分为两部分,第一部分是加密解密,第二部分是灵活的应用于中间件,我的处理方式是将获取token放到api的一个具体的controller中,将发放token与验证分离,token的失效时间,发证者,使用者等信息存放到config中. 1.配置: 在appsettings.json中增加配置 "Jwt": { "Issuer": "issuer",//随意定义 &quo

  • asp.net core集成JWT的步骤记录

    [什么是JWT] JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. JWT的官网地址:https://jwt.io/ 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT令牌在api接口中校验用户的身份以确认用户是否有访问api的权限. JWT中包含了身份认证必须的参数以及用户自定义的参数,JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名. [什么时候应该使用JSON Web令牌?] 授权:这是使用JWT的最常见方案.一旦用户登录

  • asp net core2.1如何使用jwt从原理到精通(二)

    在aspnet core中,自定义jwt管道验证 有了上一节的内容作为基础,那这点也是非常容易的,关键点在中间件,只是把上一级在测试类中的自定义验证放到中间件中来即可, 不过需要注意:中间件 的位置很重要,只有它后面的管道才会收到影响: 那我们先建一个自定义中间件类:(中间件的详细内容这里就不讲了,大家可以参考官网和其他博文) /// <summary> /// 自定义授权中间件 /// </summary> public class JwtCustomerAuthorizeMid

  • CodeFirst从零开始搭建Asp.Net Core2.0网站

    一步步教大家如何搭建Asp.Net Core2.0网站,以下所有都是建立在.NETCore2.0环境已经搭建好 右键解决方案>新建项目> 选择Web>ASP.NETCoreWeb应用程序(.NET Core) 选择Web应用程序,暂时不选择启用Docker,身份验证选择个人用户账户(会自动生成一系列和用户认证的代码) 随后生代码层次目录如下: 其中会包含身份信息的相关实现,比如相关实体信息(user)之类的,如果想对扩展微软自动的生成的用户实体类,可在Models中的Applicatio

  • Asp.Net Core2.1前后使用HttpClient的两种方式

    前言 在.Net Core应用开发中,调用第三方接口也是常有的事情,HttpClient使用人数.使用频率算是最高的一种了,在.Net Core中,HttpClient的使用方式随着版本的升级也发生了一些变化,本次就讲解一下Asp.Net Core2.1前后使用的两种方式. 一.原先HttpClient使用方式 一般来讲,喜欢要用的时候才会选择去获取资源,因此,当在有需求时才会用HttpClient去调用资源,便会使用如下这种方式或其它方式获取资源. //do something... usin

  • asp.net core2.2多用户验证与授权示例详解

    前言 asp.net core2.2 用户验证 和授权有很详细和特贴心的介绍,我感兴趣的主要是这两篇: cookie身份验证 基于角色的授权 我的项目有两类用户: 微信公众号用户,用户名为公众号的openid 企业微信的用户,用户名为企业微信的userid 每类用户中部分人员具有"Admin"角色 因为企业微信的用户有可能同时是微信公众号用户,即一个人两个名,所以需要多用户验证和授权.咱用代码说话最简洁,如下所示: public class DemoController : Contr

  • ASP.NET Core2读写InfluxDB时序数据库的方法教程

    前言 在我们很多应用中会遇到有一种基于一系列时间的数据需要处理,通过时间的顺序可以将这些数据点连成线,再通过数据统计后可以做成多纬度的报表,也可通过机器学习来实现数据的预测告警.而时序数据库就是用于存放管理这种有着时间顺序数据的,时序数据库一般都支持时序数据的快速写入.持久化.多纬度的聚合查询等基本功能. InfluxDB简介 InfluxDB是一个基于时间序列数据而开发的高性能数据存储平台,它可以对时序数据进行高吞吐量的摄取.压缩和实时查询.InfluxDB是用Go语言编写的,它会编译成一个没

  • 浅谈从ASP.NET Core2.2到3.0你可能会遇到这些问题

    趁着假期的时间所以想重新学习下微软的官方文档来巩固下基础知识.我们都知道微软目前已经发布了.NET Core3.0的第三个预览版,同时我家里的电脑也安装了vs2019.So,就用vs2019+.NET Core3.0来跟着做一下Contoso University这个WEB应用,但是在基于3.0进行操作的时候遇到了一些问题,所以我就查看了微软的<从 ASP.NET Core 迁移 2.2 到 3.0 预览版 2>这篇文档,就着今天遇到的问题,所以我整理下,希望对大伙有所帮助,当然大伙也可以直接

  • ASP.NET Core2静默获取微信公众号的用户OpenId实例代码

    前言 最近在做个微信公众号的项目,需要将入口放置在公众号二级菜单内,通过点击该菜单链接后进入到该项目中去,进入到项目后程序会自动通过微信公众号的API完成用户的OpenId获取.需求很简单,实现起来也不复杂,于是在一番折腾后需求实现了.为此,写下此文仅为初次接触的朋友提供个小小的帮助. 准备 老规矩,在开始动手前,咱们先简单介绍下实现的组成部分,如下: 微信公众号静默获取用户OpenId:要实现该功能,可以通过微信公众号提供的"网页授权"接口完成(官网描述:以snsapi_base为s

  • ASP.NET Core学习之使用JWT认证授权详解

    概述 认证授权是很多系统的基本功能 , 在以前PC的时代 , 通常是基于cookies-session这样的方式实现认证授权 , 在那个时候通常系统的用户量都不会很大, 所以这种方式也一直很好运行, 随着现在都软件用户量越来越大, 系统架构也从以前垂直扩展(增加服务器性能) -> 水平扩展(增加服务器数量) cookies-session 工作方式 客户端提交用户信息 -> 服务器识别用户 -> 服务端保存用户信息 -> 返回session-id客户端 -> 客户端保存ses

  • 详解ASP.NET Core Web Api之JWT刷新Token

    前言 如题,本节我们进入JWT最后一节内容,JWT本质上就是从身份认证服务器获取访问令牌,继而对于用户后续可访问受保护资源,但是关键问题是:访问令牌的生命周期到底设置成多久呢?见过一些使用JWT的童鞋会将JWT过期时间设置成很长,有的几个小时,有的一天,有的甚至一个月,这么做当然存在问题,如果被恶意获得访问令牌,那么可在整个生命周期中使用访问令牌,也就是说存在冒充用户身份,此时身份认证服务器当然也就是始终信任该冒牌访问令牌,若要使得冒牌访问令牌无效,唯一的方案则是修改密钥,但是如果我们这么做了,

  • ASP.NET Core 实现自动刷新JWT Token

    目录 原理 实现 结论 前言: 为了安全性考虑,我们可以设置JWT Token较短的过期时间,但是这样会导致客户端频繁地跳到登录界面,用户体验不好. 正常解决办法是增加​​refresh_token​​,客户端使用refresh_token去主动刷新JWT Token. 这里介绍一种变通的方式,自动刷新JWT Token. 原理 我们读取每个请求的​​Authorization​​头,获得当前请求的JWT Token. 检查当前token的过期时间,如果在30分钟以内,那么我们就生成一个具有新过

随机推荐