目錄後端
學有所得架構
通過前篇的介紹,你們對jwt應該有很清晰的認識了,接下來咱們如何讓代碼更清晰,以便之後面的擴展和對比;ide
首先,咱們先把一篇的自定義策略驗證進行封裝,放到一個靜態擴展方法中去,和上一節仍是有點區別,多加了一點東西,以下:ui
public static class ConfigServiceExtension { public static void AddInnerAuthorize(this IServiceCollection services, IConfiguration config) { services.AddAuthorization(option => { //自定義一些策略,原理都是基於申明key和value的值進行比較或者是否有無 #region 鍵值對對比的一些驗證策略 option.AddPolicy("onlyRober", policy => policy.RequireClaim("name", "rober")); option.AddPolicy("onlyAdminOrSuperUser", policy => policy.RequireClaim(ClaimTypes.Role, "Admin", "SuperUser")); //多申明共同,申明中包含aud:rober或者申明中值有等於Rober的均可以經過 option.AddPolicy("multiClaim", policy => policy.RequireAssertion(context => { return context.User.HasClaim("aud", config["JwtOption:Audience"]) || context.User.HasClaim(c => c.Value == config["JwtOption:Name"]); })); #endregion #region 自定義驗證策略 option.AddPolicy("ageRequire", policy => policy.Requirements.Add(new AgeRequireMent(20))); option.AddPolicy("common", policy => policy.Requirements.Add(new CommonAuthorize())); #endregion }).AddAuthentication(option => { option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(option => { if (!string.IsNullOrEmpty(config["JwtOption:SecurityKey"])) { TokenContext.securityKey = config["JwtOption:SecurityKey"]; } //設置須要驗證的項目 option.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true,//是否驗證Issuer ValidateAudience = true,//是否驗證Audience ValidateLifetime = true,//是否驗證失效時間 ValidateIssuerSigningKey = true,//是否驗證SecurityKey ValidAudience = config["JwtOption:Audience"],//Audience ValidIssuer = config["JwtOption:Issuer"],//Issuer,這兩項和前面簽發jwt的設置一致 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(TokenContext.securityKey))//拿到SecurityKey }; }); //自定義策略IOC添加 services.AddSingleton<IAuthorizationHandler, AgeRequireHandler>(); services.AddSingleton<IAuthorizationHandler, CommonAuthorizeHandler>(); } } /// <summary> /// jwt配置項目(從配置文件中初始化) /// </summary> public class JwtOption { public string Issuer { get; set; } = "rober"; public string Audience { get; set; } = "rober"; public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(50); public string SecurityKey { get; set; } = "www.rober.com(welcome you)"; public string Name { get; set; } }
第1、在之前的基礎上添加了幾個簡單的自定義策略(onlyRober,onlyAdminOrSuperUser,multiClaim),this
第2、還有個自定義策略AgeRequireMent,這個和上一篇講的相似,也是兩個重要的類組成(AgeRequireHandler 和 AgeRequireMent )請自行參考代碼,以下:spa
/// <summary> /// 訪問者年齡要求自定義策略 /// </summary> public class AgeRequireHandler : AuthorizationHandler<AgeRequireMent> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AgeRequireMent requirement) { if (!context.User.HasClaim(c => c.Type == "age")) { return Task.CompletedTask; } int.TryParse(context.User.Claims.FirstOrDefault(c => c.Type == "age").Value, out int age); if (age >= requirement.Age) { context.Succeed(requirement);//標識驗證成功 } return Task.CompletedTask; } } public class AgeRequireMent : IAuthorizationRequirement { public int Age { get; set; } public AgeRequireMent(int age) => this.Age = age; }
第3、AddJwtBearer方法中添加了驗證簽名正確的驗證項目code
ValidateIssuer = true,//是否驗證Issuer,設置值爲 ValidIssuer = config["JwtOption:Issuer"],//Issuer,這兩項和前面簽發jwt的設置一致
ValidateAudience = true,//是否驗證Audience ,設置值 ValidAudience = config["JwtOption:Audience"],//
ValidateLifetime = true,//是否驗證失效時間
ValidateIssuerSigningKey = true,//是否驗證SecurityKeyjwt
第四,把自定義策略,注入進去
blog
其次,咱們如何使用上面定義了那麼多策略呢?經過[Authorize(Policy = "策略名稱")]放在controller或者action上面rem
代碼以下:
/// <summary> /// 權限示例控制器, /// </summary> public class RAuthorizeDemoController : BaseController { private readonly JwtOption option; public RAuthorizeDemoController(IOptions<JwtOption> optionContainer) { this.option = optionContainer.Value; } /// <summary> /// 基本的簡單驗證 /// </summary> /// <returns></returns> [HttpGet] [Authorize] public ReturnMsg Get() { return ReturnMsg.Get(true, "Success"); } /// <summary> /// 基於某一策略受權 /// </summary> /// <returns></returns> [HttpGet] [Authorize(Policy = "onlyRober")] public ReturnMsg GetUser() { return ReturnMsg.Get(true); } /// <summary> /// 基於多策略受權 /// </summary> /// <returns></returns> [HttpGet] [Authorize(Policy = "multiClaim")] public ReturnMsg GetMulti() { return ReturnMsg.Get(true); } /// <summary> /// 基於某一角色受權 /// </summary> /// <returns></returns> [HttpGet] [Authorize(Roles = "Admin,SuperUser")] public ReturnMsg GetRole() { return ReturnMsg.Get(true); } /// <summary> /// 基於 role策略受權和上面GetRole效果相同 /// </summary> /// <returns></returns> [Authorize(Policy = "onlyAdminOrSuperUser")] [HttpGet] public ReturnMsg GetRoleCopy() { return ReturnMsg.Get(true); } [Authorize(Policy = "ageRequire")] [HttpGet] public ReturnMsg GetAge() { return ReturnMsg.Get(true); } [Authorize(Policy = "common")] [HttpGet] public ReturnMsg GetCommon() { return ReturnMsg.Get(true); } }
看到這裏使用內置的AuthorizeAttribute類進行各類策略驗證是否是也很簡單呢?
估計你們對各類策略的實質也有深入認識了,接下來咱們來作個對比,實際上它內部也就是那樣的實現的
jwt自定義驗證(請看上一節的自定義裏面的驗證方法中的代碼) | 使用[Authorize(Policy = "策略名稱")]驗證,請參考上面的RAuthorizeDemoController 類 | 備註 | ||
success = success && payLoad["name"]?.ToString()=="rober" | option.AddPolicy("onlyRober", policy => policy.RequireClaim("name", "rober")); |
判斷payLoad["name"]=="rober" | ||
success = success &&( payLoad["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]?.ToString()=="Admin" || payLoad["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]?.ToString()=="SuperUser") | option.AddPolicy("onlyAdminOrSuperUser", policy => policy.RequireClaim(ClaimTypes.Role, "Admin", "SuperUser")); |
|||
success = success &&( payLoad["aud"]?.ToString()=="roberAudience" || payLoad.Keys.Contains("name")) | option.AddPolicy("multiClaim", policy => policy.RequireAssertion(context => |
|||
上面都是等價的,其餘類推,無非就是正對payLoad的值進行各類對比;
到這裏jwt的驗證也接近尾聲了,是否是以爲jwt驗證很簡單啊?
不要被各類寫法給繞暈了,仔細看看它的原理,其實很是簡單,無非就是對payload的各類值進行判斷和jwt的有效性驗證;
在項目中,其實用的最多的就是上一節的內自定義jwt管道驗證和自定義策略「common」的驗證方法,我不少項目中就是這麼用的,至於用戶,角色策略用的很是少;
接下來,我將發表一個aspnet core 2.1以上版本的後端架構方面的博文,這個架構但是實戰中的架構(已經經歷20多個項目,10多年的不斷更新和刪減),不是demo;
但願經過簡單易懂的原理入門,到深刻了解,再到輕鬆愉快運用的過程,最終但願你們共同進步,共同加入,開源出去;