ASP.NET Core 使用 JWT 自定义角色/策略授权需要实现的接口
目錄
① 存儲角色/用戶所能訪問的 API
② 實現 IAuthorizationRequirement 接口
③ 實現 TokenValidationParameters
④ 生成 Token
⑤ 實現服務注入和身份認證配置
⑥ 實現登陸
⑦ 添加 API 授權策略
⑧ 實現自定義授權校驗
⑨ 一些有用的代碼
① 存儲角色/用戶所能訪問的 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());// 定義如何生成用戶的 Tokenvar tokenValidationParameters = RolePermission.GetTokenValidationParameters();配置 ASP.NET Core 的身份認證服務
需要實現三個配置
AddAuthorization 導入角色身份認證策略
AddAuthentication 身份認證類型
AddJwtBearer Jwt 認證配置
注入自定義的授權服務 PermissionHandler
注入自定義認證模型類 roleRequirement
// 添加 httpcontext 攔截services.AddSingleton<IAuthorizationHandler, PermissionHandler>();services.AddSingleton(roleRequirement);添加中間件
貌似這兩個不區分先后順序
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;}表示時間戳
總結
以上是生活随笔為你收集整理的ASP.NET Core 使用 JWT 自定义角色/策略授权需要实现的接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core on K8S深
- 下一篇: 关于 .Net Core runtime