您的位置:首页 > 编程语言 > ASP

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

2017-09-16 13:03 1106 查看

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

 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;
        }

PermissionRequirement.cs

[code=c#;toolbar:false">    /// <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.Cl 20000 aimType).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

[code=c#;toolbar:false">        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();         }

 下面定义一个控制台(.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


阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: