700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > ASP.NET Core 使用 JWT 自定义角色/策略授权需要实现的接口

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

时间:2023-12-19 15:52:39

相关推荐

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

目录

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

② 实现 IAuthorizationRequirement 接口

③ 实现 TokenValidationParameters

④ 生成 Token

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

⑥ 实现登陆

⑦ 添加 API 授权策略

⑧ 实现自定义授权校验

⑨ 一些有用的代码

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

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

可有可无。

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

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

/// &lt;summary&gt;/// 获取基于JWT的Token/// &lt;/summary&gt;/// &lt;param name="username"&gt;&lt;/param&gt;/// &lt;returns&gt;&lt;/returns&gt;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());// 定义如何生成用户的 Tokenvar tokenValidationParameters = RolePermission.GetTokenValidationParameters();

配置 Core 的身份认证服务

需要实现三个配置

AddAuthorization 导入角色身份认证策略

AddAuthentication 身份认证类型

AddJwtBearer Jwt 认证配置

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

注入自定义的授权服务 PermissionHandler

注入自定义认证模型类 roleRequirement

// 添加 httpcontext 拦截services.AddSingleton&lt;IAuthorizationHandler, PermissionHandler&gt;();services.AddSingleton(roleRequirement);

添加中间件

貌似这两个不区分先后顺序

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

⑥ 实现登陆

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

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

/// &lt;summary&gt;/// 登陆/// &lt;/summary&gt;/// &lt;param name="username"&gt;用户名&lt;/param&gt;/// &lt;param name="password"&gt;密码&lt;/param&gt;/// &lt;returns&gt;Token信息&lt;/returns&gt;[HttpPost("login")]public JsonResult Login(string username, string password){var user = UserModel.Users.FirstOrDefault(x =&gt; x.UserName == username &amp;&amp; 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 的功能。

/// &lt;summary&gt;/// 验证用户信息,进行权限授权Handler/// &lt;/summary&gt;public class PermissionHandler : AuthorizationHandler&lt;PermissionRequirement&gt;{protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,PermissionRequirement requirement){List&lt;PermissionRequirement&gt; requirements = new List&lt;PermissionRequirement&gt;();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 &lt; nowTime)context.Fail();// 是否有访问此 API 的权限var resource = ((Microsoft.AspNetCore.Routing.RouteEndpoint)context.Resource).RoutePattern;var permissions = item.Roles.Permissions.ToList();var apis = permissions.Any(x =&gt; x.Name.ToLower() == item.Roles.Name.ToLower() &amp;&amp; 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 pletedTask;}}

⑨ 一些有用的代码

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

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

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;}/// &lt;summary&gt;/// 获取用于加密 Token 的密钥/// &lt;/summary&gt;/// &lt;returns&gt;&lt;/returns&gt;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 = puteHash(Encoding.UTF8.GetBytes(source));StringBuilder sBuilder = new StringBuilder();for (int i = 0; i &lt; data.Length; i++){sBuilder.Append(data[i].ToString("x2"));}return sBuilder.ToString();}}

签发 Token

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

/// &lt;summary&gt;/// 颁发用户Token/// &lt;/summary&gt;public class JwtToken{/// &lt;summary&gt;/// 获取基于JWT的Token/// &lt;/summary&gt;/// &lt;param name="username"&gt;&lt;/param&gt;/// &lt;returns&gt;&lt;/returns&gt;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;}

表示时间戳

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。