温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》
  • 首页 > 
  • 教程 > 
  • 开发技术 > 
  • ASP.NET Core如何使用JWT自定义角色并实现策略授权需要的接口

ASP.NET Core如何使用JWT自定义角色并实现策略授权需要的接口

发布时间:2022-01-17 19:20:13 来源:亿速云 阅读:236 作者:kk 栏目:开发技术

ASP.NET Core如何使用JWT自定义角色并实现策略授权需要的接口,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

① 存储角色/用户所能访问的 API

例如

使用 List<ApiPermission> 存储角色的授权 API 列表。

可有可无。

可以把授权访问的 API 存放到 Token 中,Token 也可以只存放角色信息和用户身份信息。

    /// <summary>     /// API     /// </summary>     public class ApiPermission     {         /// <summary>         /// API名称         /// </summary>         public virtual string Name { get; set; }         /// <summary>         /// API地址         /// </summary>         public virtual string Url { get; set; }     }

② 实现 IAuthorizationRequirement 接口

IAuthorizationRequirement 接口代表了用户的身份信息,作为认证校验、授权校验使用。

事实上,IAuthorizationRequirement 没有任何要实现的内容。

namespace Microsoft.AspNetCore.Authorization {     //     // 摘要:     //     Represents an authorization requirement.     public interface IAuthorizationRequirement     {     } }

实现 IAuthorizationRequirement ,可以任意定义需要的属性,这些会作为自定义验证的便利手段。
要看如何使用,可以定义为全局标识,设置全局通用的数据。
我后面发现我这种写法不太好:

    //IAuthorizationRequirement 是 Microsoft.AspNetCore.Authorization 接口     /// <summary>     /// 用户认证信息必要参数类     /// </summary>     public class PermissionRequirement : IAuthorizationRequirement     {         /// <summary>         /// 用户所属角色         /// </summary>         public Role Roles { get;  set; } = new Role();         public void SetRolesName(string roleName)         {             Roles.Name = roleName;         }         /// <summary>         /// 无权限时跳转到此API         /// </summary>         public string DeniedAction { get; set; }         /// <summary>         /// 认证授权类型         /// </summary>         public string ClaimType { internal get; set; }         /// <summary>         /// 未授权时跳转         /// </summary>         public string LoginPath { get; set; } = "/Account/Login";         /// <summary>         /// 发行人         /// </summary>         public string Issuer { get; set; }         /// <summary>         /// 订阅人         /// </summary>         public string Audience { get; set; }         /// <summary>         /// 过期时间         /// </summary>         public TimeSpan Expiration { get; set; }         /// <summary>         /// 颁发时间         /// </summary>         public long IssuedTime { get; set; }         /// <summary>         /// 签名验证         /// </summary>         public SigningCredentials SigningCredentials { get; set; }         /// <summary>         /// 构造         /// </summary>         /// <param name="deniedAction">无权限时跳转到此API</param>         /// <param name="userPermissions">用户权限集合</param>         /// <param name="deniedAction">拒约请求的url</param>         /// <param name="permissions">权限集合</param>         /// <param name="claimType">声明类型</param>         /// <param name="issuer">发行人</param>         /// <param name="audience">订阅人</param>         /// <param name="issusedTime">颁发时间</param>         /// <param name="signingCredentials">签名验证实体</param>         public PermissionRequirement(string deniedAction, Role Role, string claimType, string issuer, string audience, SigningCredentials signingCredentials,long issusedTime, TimeSpan expiration)         {             ClaimType = claimType;             DeniedAction = deniedAction;             Roles = Role;             Issuer = issuer;             Audience = audience;             Expiration = expiration;             IssuedTime = issusedTime;             SigningCredentials = signingCredentials;         }     }

③ 实现 TokenValidationParameters

Token 的信息配置

        public static TokenValidationParameters GetTokenValidationParameters()         {             var tokenValida = new TokenValidationParameters             {                 // 定义 Token 内容                 ValidateIssuerSigningKey = true,                 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthConfig.SecurityKey)),                 ValidateIssuer = true,                 ValidIssuer = AuthConfig.Issuer,                 ValidateAudience = true,                 ValidAudience = AuthConfig.Audience,                 ValidateLifetime = true,                 ClockSkew = TimeSpan.Zero,                 RequireExpirationTime = true             };             return tokenValida;         }

④ 生成 Token

用于将用户的身份信息(Claims)和角色授权信息(PermissionRequirement)存放到 Token 中。

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

⑤ 实现服务注入和身份认证配置

从别的变量导入配置信息,可有可无

            // 设置用于加密 Token 的密钥             // 配置角色权限              var roleRequirement = RolePermission.GetRoleRequirement(AccountHash.GetTokenSecurityKey());             // 定义如何生成用户的 Token             var tokenValidationParameters = RolePermission.GetTokenValidationParameters();

配置 ASP.NET Core 的身份认证服务

需要实现三个配置

  • AddAuthorization 导入角色身份认证策略

  • AddAuthentication 身份认证类型

  • AddJwtBearer Jwt 认证配置

            // 导入角色身份认证策略             services.AddAuthorization(options =>             {                 options.AddPolicy("Permission",                    policy => policy.Requirements.Add(roleRequirement));                 // ↓ 身份认证类型             }).AddAuthentication(options =>             {                 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;                 options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;                 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;                 // ↓ Jwt 认证配置             })             .AddJwtBearer(options =>             {                 options.TokenValidationParameters = tokenValidationParameters;                 options.SaveToken = true;                 options.Events = new JwtBearerEvents()                 {                     // 在安全令牌通过验证和ClaimsIdentity通过验证之后调用                     // 如果用户访问注销页面                     OnTokenValidated = context =>                     {                         if (context.Request.Path.Value.ToString() == "/account/logout")                         {                             var token = ((context as TokenValidatedContext).SecurityToken as JwtSecurityToken).RawData;                         }                         return Task.CompletedTask;                     }                 };             });

注入自定义的授权服务 PermissionHandler

注入自定义认证模型类 roleRequirement

            // 添加 httpcontext 拦截             services.AddSingleton<IAuthorizationHandler, PermissionHandler>();             services.AddSingleton(roleRequirement);

添加中间件

在微软官网看到例子是这样的。。。但是我测试发现,客户端携带了 Token 信息,请求通过验证上下文,还是失败,这样使用会返回403。

    app.UseAuthentication();     app.UseAuthorization();

发现这样才OK:

            app.UseAuthorization();             app.UseAuthentication();

⑥ 实现登陆

可以在颁发 Token 时把能够使用的 API 存储进去,但是这种方法不适合 API 较多的情况。

可以存放 用户信息(Claims)和角色信息,后台通过角色信息获取授权访问的 API 列表。

        /// <summary>         /// 登陆         /// </summary>         /// <param name="username">用户名</param>         /// <param name="password">密码</param>         /// <returns>Token信息</returns>         [HttpPost("login")]         public JsonResult Login(string username, string password)         {             var user = UserModel.Users.FirstOrDefault(x => x.UserName == username && x.UserPossword == password);             if (user == null)                 return new JsonResult(                     new ResponseModel                     {                         Code = 0,                         Message = "登陆失败!"                     });             // 配置用户标识             var userClaims = new Claim[]             {                 new Claim(ClaimTypes.Name,user.UserName),                 new Claim(ClaimTypes.Role,user.Role),                 new Claim(ClaimTypes.Expiration,DateTime.Now.AddMinutes(_requirement.Expiration.TotalMinutes).ToString()),             };             _requirement.SetRolesName(user.Role);             // 生成用户标识             var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);             identity.AddClaims(userClaims);             var token = JwtToken.BuildJwtToken(userClaims, _requirement);             return new JsonResult(                 new ResponseModel                 {                     Code = 200,                     Message = "登陆成功!请注意保存你的 Token 凭证!",                     Data = token                 });         }

⑦ 添加 API 授权策略

    [Authorize(Policy = "Permission")]

⑧ 实现自定义授权校验

要实现自定义 API 角色/策略授权,需要继承 AuthorizationHandler<TRequirement>

里面的内容是完全自定义的, AuthorizationHandlerContext 是认证授权的上下文,在此实现自定义的访问授权认证。

也可以加上自动刷新 Token 的功能。

    /// <summary>     /// 验证用户信息,进行权限授权Handler     /// </summary>     public class PermissionHandler : AuthorizationHandler<PermissionRequirement>     {         protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,                                                        PermissionRequirement requirement)         {             List<PermissionRequirement> requirements = new List<PermissionRequirement>();             foreach (var item in context.Requirements)             {                 requirements.Add((PermissionRequirement)item);             }             foreach (var item in requirements)             {                 // 校验 颁发和接收对象                 if (!(item.Issuer == AuthConfig.Issuer ?                     item.Audience == AuthConfig.Audience ?                     true : false : false))                 {                     context.Fail();                 }                 // 校验过期时间                 var nowTime = DateTimeOffset.Now.ToUnixTimeSeconds();                 var issued = item.IssuedTime +Convert.ToInt64(item.Expiration.TotalSeconds);                 if (issued < nowTime)                     context.Fail();                 // 是否有访问此 API 的权限                 var resource = ((Microsoft.AspNetCore.Routing.RouteEndpoint)context.Resource).RoutePattern;                 var permissions = item.Roles.Permissions.ToList();                 var apis = permissions.Any(x => x.Name.ToLower() == item.Roles.Name.ToLower() && x.Url.ToLower() == resource.RawText.ToLower());                 if (!apis)                     context.Fail();                 context.Succeed(requirement);                 // 无权限时跳转到某个页面                 //var httpcontext = new HttpContextAccessor();                 //httpcontext.HttpContext.Response.Redirect(item.DeniedAction);             }             context.Succeed(requirement);             return Task.CompletedTask;         }     }

⑨ 一些有用的代码

将字符串生成哈希值,例如密码。

为了安全,删除字符串里面的特殊字符,例如 "'$

    public static class AccountHash     {         // 获取字符串的哈希值         public static string GetByHashString(string str)         {             string hash = GetMd5Hash(str.Replace("\"", String.Empty)                 .Replace("\'", String.Empty)                 .Replace("$", String.Empty));             return hash;         }         /// <summary>         /// 获取用于加密 Token 的密钥         /// </summary>         /// <returns></returns>         public static SigningCredentials GetTokenSecurityKey()         {             var securityKey = new SigningCredentials(                 new SymmetricSecurityKey(                     Encoding.UTF8.GetBytes(AuthConfig.SecurityKey)), SecurityAlgorithms.HmacSha256);             return securityKey;         }         private static string GetMd5Hash(string source)         {             MD5 md5Hash = MD5.Create();             byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(source));             StringBuilder sBuilder = new StringBuilder();             for (int i = 0; i < data.Length; i++)             {                 sBuilder.Append(data[i].ToString("x2"));             }             return sBuilder.ToString();         }     }

签发 Token

PermissionRequirement 不是必须的,用来存放角色或策略认证信息,Claims 应该是必须的。

    /// <summary>     /// 颁发用户Token     /// </summary>     public class JwtToken     {         /// <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;         }

表示时间戳

// Unix 时间戳 DateTimeOffset.Now.ToUnixTimeSeconds(); // 检验 Token 是否过期 // 将 TimeSpan 转为 Unix 时间戳 Convert.ToInt64(TimeSpan); DateTimeOffset.Now.ToUnixTimeSeconds() + Convert.ToInt64(TimeSpan);

ASP.NET 是什么

ASP.NET 是开源,跨平台,高性能,轻量级的 Web 应用构建框架,常用于通过 HTML、CSS、JavaScript 以及服务器脚本来构建网页和网站。

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI