博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
asp.net core 2.0 web api基于JWT自定义策略授权
阅读量:6294 次
发布时间:2019-06-22

本文共 14738 字,大约阅读时间需要 49 分钟。

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上:

源码如下:

JwtToken.cs

        ///         /// 获取基于JWT的Token        ///         /// 
        /// 
        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

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

PermissionRequirement.cs

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

 自定义策略类PermissionHandler.cs

    ///     /// 权限授权Handler    ///     public class PermissionHandler : AuthorizationHandler
    {            /// 
        /// 验证方案提供对象        ///         public IAuthenticationSchemeProvider Schemes { get; set; }        /// 
        /// 自定义策略参数        ///         public PermissionRequirement Requirement        { get; set; }        /// 
        /// 构造        ///         /// 
        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
();            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
 {                              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
();            services.AddMvc();        }

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

    [Authorize("Permission")]

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

    [Authorize("Permission")]    public class PermissionController : Controller    {        ///         /// 自定义策略参数        ///         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    {        ///         /// 访问Url        ///         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://xypta.baihongyu.com/

你可能感兴趣的文章
正文提取算法
查看>>
轻松学PHP
查看>>
Linux中的网络监控命令
查看>>
this的用法
查看>>
windows下安装redis
查看>>
CentOS7 yum 安装git
查看>>
启动日志中频繁出现以下信息
查看>>
httpd – 对Apache的DFOREGROUND感到困惑
查看>>
分布式锁的一点理解
查看>>
idea的maven项目,install下载重复下载本地库中已有的jar包,而且下载后jar包都是lastupdated问题...
查看>>
2019测试指南-web应用程序安全测试(二)指纹Web服务器
查看>>
树莓派3链接wifi
查看>>
js面向对象编程
查看>>
Ruby中类 模块 单例方法 总结
查看>>
jQuery的validate插件
查看>>
5-4 8 管道符 作业控制 shell变量 环境变量配置
查看>>
Enumberable
查看>>
开发者论坛一周精粹(第五十四期) 求购备案服务号1枚!
查看>>
validate表单验证及自定义方法
查看>>
javascript 中出现missing ) after argument list的错误
查看>>