asp.net core 2.0 web api基于JWT自定义策略授权

JWT(json web token)是一种基于json的身份验证机制,流程如下:

 

 

通过登录,来获取Token,再在之后每次请求的Header中追加Authorization为Token的凭据,服务端验证通过即可能获取想要访问的资源。关于JWT的技术,可参考网络上文章,这里不作详细说明,

这篇博文,主要说明在asp.net core 2.0中,基于jwt的web api的权限设置,即在asp.net core中怎么用JWT,再次就是不同用户或角色因为权限问题,即使援用Token,也不能访问不该访问的资源。

基本思路是我们自定义一个策略,来验证用户,和验证用户授权,PermissionRequirement是验证传输授权的参数。在Startup的ConfigureServices注入验证(Authentication),授权(Authorization),和JWT(JwtBearer)

自定义策略:

已封闭成AuthorizeRolicy.JWT nuget包,并发布到nuget上:

https://www.nuget.org/packages/AuthorizePolicy.JWT/

源码如下:

JwtToken.cs


/// <summary>
/// 获取基于JWT的Token
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public  static  dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement)
{
     var  now = DateTime.UtcNow;
     var  jwt =  new  JwtSecurityToken(
         issuer: permissionRequirement.Issuer,
         audience: permissionRequirement.Audience,
         claims: claims,
         notBefore: now,
         expires: now.Add(permissionRequirement.Expiration),
         signingCredentials: permissionRequirement.SigningCredentials
     );
     var  encodedJwt =  new  JwtSecurityTokenHandler().WriteToken(jwt);
     var  response =  new
     {
         Status =  true ,
         access_token = encodedJwt,
         expires_in = permissionRequirement.Expiration.TotalMilliseconds,
         token_type =  "Bearer"
     };
     return  response;
}

  

Permission.cs


/// <summary>
/// 用户或角色或其他凭据实体
/// </summary>
public  class  Permission
{
     /// <summary>
     /// 用户或角色或其他凭据名称
     /// </summary>
     public  virtual  string  Name
     get set ; }
     /// <summary>
     /// 请求Url
     /// </summary>
     public  virtual  string  Url
     get set ; }
}

  PermissionRequirement.cs


/// <summary>
/// 必要参数类
/// </summary>
public  class  PermissionRequirement : IAuthorizationRequirement
{
     /// <summary>
     /// 用户权限集合
     /// </summary>
     public  List<Permission> Permissions {  get private  set ; }
     /// <summary>
     /// 无权限action
     /// </summary>
     public  string  DeniedAction {  get set ; }
     /// <summary>
     /// 认证授权类型
     /// </summary>
     public  string  ClaimType {  internal  get set ; }
     /// <summary>
     /// 请求路径
     /// </summary>
     public  string  LoginPath {  get set ; } =  "/Api/Login" ;
     /// <summary>
     /// 发行人
     /// </summary>
     public  string  Issuer {  get set ; }
     /// <summary>
     /// 订阅人
     /// </summary>
     public  string  Audience {  get set ; }
     /// <summary>
     /// 过期时间
     /// </summary>
     public  TimeSpan Expiration {  get set ; } = TimeSpan.FromMinutes(5000);
     /// <summary>
     /// 签名验证
     /// </summary>
     public  SigningCredentials SigningCredentials {  get set ; }
     /// <summary>
     /// 构造
     /// </summary>
     /// <param name="deniedAction">无权限action</param>
     /// <param name="userPermissions">用户权限集合</param>
     /// <summary>
     /// 构造
     /// </summary>
     /// <param name="deniedAction">拒约请求的url</param>
     /// <param name="permissions">权限集合</param>
     /// <param name="claimType">声明类型</param>
     /// <param name="issuer">发行人</param>
     /// <param name="audience">订阅人</param>
     /// <param name="signingCredentials">签名验证实体</param>
     public  PermissionRequirement( string  deniedAction, List<Permission> permissions,  string  claimType,  string  issuer,  string  audience, SigningCredentials signingCredentials)
     {
         ClaimType = claimType;
         DeniedAction = deniedAction;
         Permissions = permissions;
         Issuer = issuer;
         Audience = audience;
         SigningCredentials = signingCredentials;
     }
}

 自定义策略类PermissionHandler.cs


/// <summary>
/// 权限授权Handler
/// </summary>
public  class  PermissionHandler : AuthorizationHandler<PermissionRequirement>
{   
     /// <summary>
     /// 验证方案提供对象
     /// </summary>
     public  IAuthenticationSchemeProvider Schemes {  get set ; }
     /// <summary>
     /// 自定义策略参数
     /// </summary>
     public  PermissionRequirement Requirement
     get set ; }
     /// <summary>
     /// 构造
     /// </summary>
     /// <param name="schemes"></param>
     public  PermissionHandler(IAuthenticationSchemeProvider schemes)
     {
         Schemes = schemes;
     }  
     protected  override  async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
     {
         赋值用户权限      
         Requirement = requirement;
         //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息
         var  httpContext = (context.Resource  as  Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext).HttpContext;
         //请求Url
         var  questUrl = httpContext.Request.Path.Value.ToLower(); 
         //判断请求是否停止
         var  handlers = httpContext.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
         foreach  ( var  scheme  in  await Schemes.GetRequestHandlerSchemesAsync())
         {
             var  handler = await handlers.GetHandlerAsync(httpContext, scheme.Name)  as  IAuthenticationRequestHandler;
             if  (handler !=  null  && await handler.HandleRequestAsync())
             {
                 context.Fail();
                 return ;
             }
         }
         //判断请求是否拥有凭据,即有没有登录
         var  defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
         if  (defaultAuthenticate !=  null )
         {
             var  result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
             //result?.Principal不为空即登录成功
             if  (result?.Principal !=  null )
             {
                 httpContext.User = result.Principal;
                 //权限中是否存在请求的url
                 if  (Requirement.Permissions.GroupBy(g => g.Url).Where(w => w.Key.ToLower() == questUrl).Count() > 0)
                 {
                     var  name = httpContext.User.Claims.SingleOrDefault(s => s.Type == requirement.ClaimType).Value;
                     //验证权限
                     if  (Requirement.Permissions.Where(w => w.Name == name && w.Url.ToLower() == questUrl).Count() <= 0)
                     {
                         //无权限跳转到拒绝页面
                         httpContext.Response.Redirect(requirement.DeniedAction);
                     }
                 }
                 context.Succeed(requirement);
                 return ;
             }
         }
         //判断没有登录时,是否访问登录的url,并且是Post请求,并助是form表单提交类型,否则为失败
         if  (!questUrl.Equals(Requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals( "POST" )
            || !httpContext.Request.HasFormContentType))
         {
             context.Fail();
             return ;
         }
         context.Succeed(requirement);    
     }
}

  

新建asp.net core 2.0的web api项目,并在项目添加AuthorizePolicy.JWT如图

先设置配置文件,用户可以定义密匙和发生人,订阅人

  "Audience": {

    "Secret": "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",

    "Issuer": "gsw",

    "Audience": "everone"

  }

在ConfigureServices中注入验证(Authentication),授权(Authorization),和JWT(JwtBearer)

Startup.cs


public  void  ConfigureServices(IServiceCollection services)
{
     //读取配置文件
     var  audienceConfig = Configuration.GetSection( "Audience" );
     var  symmetricKeyAsBase64 = audienceConfig[ "Secret" ];
     var  keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
     var  signingKey =  new  SymmetricSecurityKey(keyByteArray);
     var  tokenValidationParameters =  new  TokenValidationParameters
     {
         ValidateIssuerSigningKey =  true ,
         IssuerSigningKey = signingKey,
         ValidateIssuer =  true ,
         ValidIssuer = audienceConfig[ "Issuer" ],
         ValidateAudience =  true ,
         ValidAudience = audienceConfig[ "Audience" ],
         ValidateLifetime =  true ,
         ClockSkew = TimeSpan.Zero
     };
     var  signingCredentials =  new  SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
     services.AddAuthorization(options =>
     {
         //这个集合模拟用户权限表,可从数据库中查询出来
         var  permission =  new  List<Permission> {
                       new  Permission {  Url= "/" , Name= "admin" },
                       new  Permission {  Url= "/api/values" , Name= "admin" },
                       new  Permission {  Url= "/" , Name= "system" },
                       new  Permission {  Url= "/api/values1" , Name= "system" }
                   };
         //如果第三个参数,是ClaimTypes.Role,上面集合的每个元素的Name为角色名称,如果ClaimTypes.Name,即上面集合的每个元素的Name为用户名
         var  permissionRequirement =  new  PermissionRequirement( "/api/denied" , permission, ClaimTypes.Role, audienceConfig[ "Issuer" ], audienceConfig[ "Audience" ], signingCredentials);
         options.AddPolicy( "Permission" ,
                   policy => policy.Requirements.Add(permissionRequirement));
     }).AddAuthentication(options =>
     {
         options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
         options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
     })
     .AddJwtBearer(o =>
     {
         //不使用https
         o.RequireHttpsMetadata =  false ;
         o.TokenValidationParameters = tokenValidationParameters;
     });
     //注入授权Handler
     services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
     services.AddMvc();
}

  

在需要授的Controller上添加授权特性

    [Authorize("Permission")]

 

PermissionController类有两个方法,一个是登录,验证用户名和密码是否正确,如果正确就发放Token,如果失败,验证失败,别一个成功登后的无权限导航action。


[Authorize( "Permission" )]
public  class  PermissionController : Controller
{
     /// <summary>
     /// 自定义策略参数
     /// </summary>
     PermissionRequirement _requirement;
     public  PermissionController(IAuthorizationHandler authorizationHander)
     {
         _requirement = (authorizationHander  as  PermissionHandler).Requirement;
     }
     [AllowAnonymous]
     [HttpPost( "/api/login" )]
     public  IActionResult Login( string  username, string  password, string  role)
     {
         var  isValidated = username ==  "gsw"  && password ==  "111111" ;
         if  (!isValidated)
         {
             return  new  JsonResult( new
             {
                 Status =  false ,
                 Message =  "认证失败"
             });
         }
         else
         {
             //如果是基于角色的授权策略,这里要添加用户;如果是基于角色的授权策略,这里要添加角色
             var  claims = new  Claim[]{  new  Claim(ClaimTypes.Name, username), new  Claim(ClaimTypes.Role, role) };
             //用户标识
             var  identity =  new  ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
             identity.AddClaims(claims);
             //登录
             HttpContext.SignInAsync(JwtBearerDefaults.AuthenticationScheme,  new  ClaimsPrincipal(identity));
             var  token = JwtToken.BuildJwtToken(claims, _requirement);
             return  new  JsonResult(token);
         }
     }
     [AllowAnonymous]
     [HttpGet( "/api/denied" )]
     public  IActionResult Denied()
     {
         return  new  JsonResult( new
         {
             Status =  false ,
             Message =  "你无权限访问"
         });
     }
}

  

下面定义一个控制台(.NetFramewrok)程序,用RestSharp来访问我们定义的web api,其中1为admin角色登录,2为system角色登录,3为错误用户密码登录,4是一个查询功能,在startup.cs中,admin角色是具有查询/api/values的权限的,所以用admin登录是能正常访问的,用system登录,能成功登录,但没有权限访问/api/values,用户名密码错误,访问/aip/values,直接是没有授权的


class  Program
   {
       /// <summary>
       /// 访问Url
       /// </summary>
       static  string  _url =  "http://localhost:39286" ;
       static  void  Main( string [] args)
       {
           dynamic token =  null ;
           while  ( true )
           {
               Console.WriteLine( "1、登录【admin】 2、登录【system】 3、登录【错误用户名密码】 4、查询数据 " );
               var  mark = Console.ReadLine();
               var  stopwatch =  new  Stopwatch();
               stopwatch.Start();
               switch  (mark)
               {
                   case  "1" :
                       token = AdminLogin();
                       break ;
                   case  "2" :
                       token = SystemLogin();
                       break ;
                   case  "3" :
                       token = NullLogin();
                       break ;
                   case  "4" :
                       AdminInvock(token);
                       break ;
               }
               stopwatch.Stop();
               TimeSpan timespan = stopwatch.Elapsed;
               Console.WriteLine($ "间隔时间:{timespan.TotalSeconds}" );
           }
       }
       static  dynamic NullLogin()
       {
           var  loginClient =  new  RestClient(_url);
           var  loginRequest =  new  RestRequest( "/api/login" , Method.POST);
           loginRequest.AddParameter( "username" "gswaa" );
           loginRequest.AddParameter( "password" "111111" );
           //或用用户名密码查询对应角色
           loginRequest.AddParameter( "role" "system" );
           IRestResponse loginResponse = loginClient.Execute(loginRequest);
           var  loginContent = loginResponse.Content;
           Console.WriteLine(loginContent);
           return  Newtonsoft.Json.JsonConvert.DeserializeObject(loginContent);
       }
       static  dynamic SystemLogin()
       {
           var  loginClient =  new  RestClient(_url);
           var  loginRequest =  new  RestRequest( "/api/login" , Method.POST);
           loginRequest.AddParameter( "username" "gsw" );
           loginRequest.AddParameter( "password" "111111" );
           //或用用户名密码查询对应角色
           loginRequest.AddParameter( "role" "system" );
           IRestResponse loginResponse = loginClient.Execute(loginRequest);
           var  loginContent = loginResponse.Content;
           Console.WriteLine(loginContent);
           return  Newtonsoft.Json.JsonConvert.DeserializeObject(loginContent);
       }
       static  dynamic AdminLogin()
       {
           var  loginClient =  new  RestClient(_url);
           var  loginRequest =  new  RestRequest( "/api/login" , Method.POST);
           loginRequest.AddParameter( "username" "gsw" );
           loginRequest.AddParameter( "password" "111111" );
           //或用用户名密码查询对应角色
           loginRequest.AddParameter( "role" "admin" );
           IRestResponse loginResponse = loginClient.Execute(loginRequest);
           var  loginContent = loginResponse.Content;
           Console.WriteLine(loginContent);
           return  Newtonsoft.Json.JsonConvert.DeserializeObject(loginContent);
       }
       static  void  AdminInvock(dynamic token)
       {
           var  client =  new  RestClient(_url);
           //这里要在获取的令牌字符串前加Bearer
           string  tk =  "Bearer "  + Convert.ToString(token?.access_token);
           client.AddDefaultHeader( "Authorization" , tk);
           var  request =  new  RestRequest( "/api/values" , Method.GET);
           IRestResponse response = client.Execute(request);
           var  content = response.Content;
           Console.WriteLine($ "状态:{response.StatusCode}  返回结果:{content}" );
       }
   }

  

运行结果:

 

源码:https://github.com/axzxs2001/AuthorizePolicy.JWT

原文地址:http://www.cnblogs.com/axzxs2001/p/7530929.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323587.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BATJ面试必会|Jvm 虚拟机篇

转载自 BATJ面试必会|Jvm 虚拟机篇 目录 一、运行时数据区域 程序计数器 Java 虚拟机栈 本地方法栈 堆 方法区 运行时常量池 直接内存 二、垃圾收集 判断一个对象是否可被回收 引用类型 垃圾收集算法 垃圾收集器 三、内存分配与回收策略 Minor GC 和 Full GC 内存…

让网页背景颜色改变

如何改变背景的颜色呢&#xff0c;这里提供一个方法 <!DOCTYPE html> <html><head><style type"text/css">body {background-color: red}p {margin-left: 1px}</style><title>我yi癫狂</title></head><body>…

糊涂工具类真是场景下请求http接口的案例

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 现在是&#xff1a;2022年7月7日13:46:07 前言 今天有个这样的需求&#xff0c;PC端需要查看一下哪些天有数据&#xff0c;但是哪些有有没有数据我这边还看不出来&#xff0c;得请求别的系…

体验 ASP.NET Core 中的多语言支持(Localization)

首先在 Startup 的 ConfigureServices 中添加 AddLocalization 与 AddViewLocalization 以及配置 RequestLocalizationOptions &#xff08;这里假设使用英文与中文&#xff09;&#xff1a; public void ConfigureServices(IServiceCollection services) { services.AddLoca…

java中复杂业务情况下的集合操作(增减集合同步数据)

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 现在是&#xff1a;2022年7月5日16:14:28 前言 今天分享个案例&#xff0c;需求是这样的&#xff1a;一个团组中是可以包含多个会员&#xff0c;在给团组创建训练方案时&#xff0c;本质上…

springboot整合spring @Cache和Redis

转载自 springboot整合spring Cache和Redis spring基于注解的缓存 对于缓存声明&#xff0c;spring的缓存提供了一组java注解: Cacheable:触发缓存写入。CacheEvict:触发缓存清除。CachePut:更新缓存(不会影响到方法的运行)。Caching:重新组合要应用于方法的多个缓存操作。…

段落分开

分三段 <!DOCTYPE html> <html><head></head><body><p>This is a paragraph.</p> <p>This is another paragraph.</p> <p>这是网页设计中定义段落的标记&#xff0c;称为开始标记&#xff0c;称为结束标记。把一…

辞旧迎新,新工作感悟!

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 现在是&#xff1a;2022年6月21日22:33:34 公众号又好久没有更新啦。从以前的日更&#xff0c;到后来的周更&#xff0c;再到后来的月更……不知道会不会到不更的结局。。。 最近换工作了…

ASPNET Core 2.x中的Kestrel服务器

Kestrel是一个基于libuv的跨平台ASP.NET Core web服务器&#xff0c;libuv是一个跨平台的异步I/O库。ASP.NET Core模板项目使用Kestrel作为默认的web服务器。 Kestrel支持以下功能&#xff1a; HTTPS用于启用不透明升级的WebSockets位于Nginx之后的高性能Unix sockets Kestr…

全文搜索!收藏这篇Solr ElasticSearch 长文就可以搞定

转载自 全文搜索&#xff01;收藏这篇Solr ElasticSearch 长文就可以搞定 摘自&#xff1a;JaJian博кē Java后端技术编者说&#xff1a;文章从浅到深&#xff0c;描述了什么是全文搜索&#xff0c;为什么要使用全文搜索&#xff0c;Solr和ElasticSearch的发展和比较。文章比…

网页弹框弹出

完成这个功能需要在 弹框弹出 点击之后 才会显示内容 <!DOCTYPE html> <html><head><title>对话框测试</title><script>alert("对话框测试");</script></head><body>对话框测试&#xff1a;</body> …

Azure School与开源

距离Azure School(https://school.azure.cn/) 的正式上线时间&#xff0c;2017.8.3&#xff0c;已经过去了20多天了。这20多天得到了很多有帮助的建议和反馈&#xff0c;小伙伴们都很给力&#xff0c;互相帮助扶持&#xff0c;调整产品不正确的地方&#xff0c;快速迭代新的功能…

select count(*)底层究竟干了啥么?

转载自 select count(*)底层究竟干了啥么&#xff1f; “SELECT COUNT( * ) FROM t” 是个再常见不过的 SQL 需求了。在 MySQL 的使用规范中&#xff0c;我们一般使用事务引擎 InnoDB 作为(一般业务)表的存储引擎&#xff0c;在此前提下&#xff0c;COUNT( * )操作的时间复杂…

为什么不应该重写service方法

转载自 为什么不应该重写service方法 故事通常是这样开始的&#xff1a; 从前&#xff0c;有一个程序猿&#xff0c;他语重心长地对孙子说&#xff1a;“孩子&#xff0c;要是你以后写servlet&#xff0c;最好不要重写service方法啊” 孙子大为不解&#xff0c;程序猿又说&…

.net core之ACG小站爬虫(二)

紧跟着上一节说的文章&#xff0c;虽然已经放出了所写的全代码&#xff0c;但还是再解释一下另外一个页面的请求和分析过程吧。PS&#xff1a;又可以愉快的水一章了&#xff0c;咕嘿嘿。页面分析上回说到下载按钮的href属性是javascript:;伪协议&#xff0c;导致了新打开的页面…

.net core之ACG小站爬虫(一)

想到好久没写过.net的代码了&#xff0c;因此就尝试来写一写.net的代码。此外&#xff0c;也想要熟悉一下Phantomjs。环境配置.net core下载。可选的可以下载宇宙大IDEVisual Studio&#xff0c;当然更加推荐使用Visual Studio Code进行代码的书写。Phantomjs。这个不用说了&am…

数组:完成等差等比数列,及其他数列

有没有想过用c语言来编写一些数列呢&#xff0c;编写数列&#xff0c;数组是最好的选择。 等差&#xff1a; #include<stdio.h> main(){ int a[1000],b,i;a[0]1;scanf("%d",&b);for(i0;i<b;i){a[i1]a[i]2;}for(i0;i<b;i){printf("a%d%-8d\t&q…

Spring MVC竟然有5种参数绑定的方式?你知道几种?

转载自 Spring MVC竟然有5种参数绑定的方式&#xff1f;你知道几种&#xff1f; SpringMVC参数绑定&#xff0c;简单来说就是将客户端请求的key/value数据绑定到controller方法的形参上&#xff0c;然后就可以在controller中使用该参数了下面通过5个常用的注解演示下如何进行参…

微服务中如何切换配置文件、部署

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 最近改了个微服务的项目&#xff0c;在本身的业务上加上一个演示的业务。 以前没有弄过微服务的项目&#xff0c;这次改完之后&#xff0c;部署花了不少时间&#xff0c;一方面是因为服务器…

asp.net core mvc View Component 应用

ViewComponent1、View 组件介绍在ASP.NET CORE MVC中,View组件有点类似于partial views,但是他们更强大&#xff0c;View组件不能使用model binding,当你调用它的时候仅仅依赖与你提供的数据一个View组件特点&#xff1a;.呈现一大块而不是一个整体的响应。 .包含在控制器和视…