目錄html
學有所得數據庫
有了上一節的內容做爲基礎,那這點也是很是容易的,關鍵點在中間件,只是把上一級在測試類中的自定義驗證放到中間件中來便可,json
不過須要注意:中間件 的位置很重要,只有它後面的管道纔會收到影響;api
那咱們先建一個自定義中間件類:(中間件的詳細內容這裏就不講了,你們能夠參考官網和其餘博文)緩存
/// <summary> /// 自定義受權中間件 /// </summary> public class JwtCustomerAuthorizeMiddleware { private readonly RequestDelegate next; public JwtCustomerAuthorizeMiddleware(RequestDelegate next, string secret, List<string> anonymousPathList) { #region 設置自定義jwt 的祕鑰 if(!string.IsNullOrEmpty(secret)) { TokenContext.securityKey = secret; } #endregion this.next = next; UserContext.AllowAnonymousPathList.AddRange(anonymousPathList); } public async Task Invoke(HttpContext context, UserContext userContext,IOptions<JwtOption> optionContainer) { if (userContext.IsAllowAnonymous(context.Request.Path)) { await next(context); return; } var option = optionContainer.Value; #region 身份驗證,並設置用戶Ruser值 var result = context.Request.Headers.TryGetValue("Authorization", out StringValues authStr); if (!result || string.IsNullOrEmpty(authStr.ToString())) { throw new UnauthorizedAccessException("未受權"); } result = TokenContext.Validate(authStr.ToString().Substring("Bearer ".Length).Trim(), payLoad => { var success = true; //能夠添加一些自定義驗證,用法參照測試用例 //驗證是否包含aud 並等於 roberAudience success = success && payLoad["aud"]?.ToString() == option.Audience; if (success) { //設置Ruse值,把user信息放在payLoad中,(在獲取jwt的時候把當前用戶存放在payLoad的ruser鍵中) //若是用戶信息比較多,建議放在緩存中,payLoad中存放緩存的Key值 userContext.TryInit(payLoad["ruser"]?.ToString()); } return success; }); if (!result) { throw new UnauthorizedAccessException("未受權"); } #endregion #region 權限驗證 if (!userContext.Authorize(context.Request.Path)) { throw new UnauthorizedAccessException("未受權"); } #endregion await next(context); } }
上面這個中間件中有個UserContext上線文,這個類主要管理當前用戶信息和權限,其餘信息暫時無論;咱們先看一下這個中間件的驗證流程如何:app
該中間件主要是針對訪問的路徑進行驗證,固然你也能夠針對其餘信息進行驗證,好比(控制器名稱,動做名稱,等)async
自定義中間件使用jwt驗證就這些內容,是否是感受很清晰,很簡單,有木有;ide
中間已經完成了,那接下來咱們來使用它,咱們再startup中的Configure方法中添加以下代碼性能
app.UseMiddleware<JwtCustomerAuthorizeMiddleware>(Configuration["JwtOption:SecurityKey"], new List<string>() { "/api/values/getjwt","/" });測試
固然上面可匿名訪問的url也能夠定義在appsetting.json文件中,能夠自行嘗試
建立自定義策略的詳細介紹能夠參考官網,這裏就不詳細介紹,
首先咱們上代碼,建立自定義策略很是重要的兩個類,以下:
public class CommonAuthorizeHandler : AuthorizationHandler<CommonAuthorize> { /// <summary> /// 經常使用自定義驗證策略,模仿自定義中間件JwtCustomerauthorizeMiddleware的驗證範圍 /// </summary> /// <param name="context"></param> /// <param name="requirement"></param> /// <returns></returns> protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CommonAuthorize requirement) { var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext; var userContext = httpContext.RequestServices.GetService(typeof(UserContext)) as UserContext; var jwtOption = (httpContext.RequestServices.GetService(typeof(IOptions<JwtOption>)) as IOptions<JwtOption>).Value; #region 身份驗證,並設置用戶Ruser值 var result = httpContext.Request.Headers.TryGetValue("Authorization", out StringValues authStr); if (!result || string.IsNullOrEmpty(authStr.ToString())) { return Task.CompletedTask; } result = TokenContext.Validate(authStr.ToString().Substring("Bearer ".Length).Trim(), payLoad => { var success = true; //能夠添加一些自定義驗證,用法參照測試用例 //驗證是否包含aud 並等於 roberAudience success = success && payLoad["aud"]?.ToString() == jwtOption.Audience; if (success) { //設置Ruse值,把user信息放在payLoad中,(在獲取jwt的時候把當前用戶存放在payLoad的ruser鍵中) //若是用戶信息比較多,建議放在緩存中,payLoad中存放緩存的Key值 userContext.TryInit(payLoad["ruser"]?.ToString()); } return success; }); if (!result) { return Task.CompletedTask; } #endregion #region 權限驗證 if (!userContext.Authorize(httpContext.Request.Path)) { return Task.CompletedTask; } #endregion context.Succeed(requirement); return Task.CompletedTask; } } public class CommonAuthorize: IAuthorizationRequirement { }
其中兩個重要的類是哪兩個呢?他們的做用又是什麼呢?
CommonAuthorize: IAuthorizationRequirement,至於取什麼名字,本身定義,但必須繼承IAuthorizationRequirement,這類主要是承載一些初始化值,讓後傳遞到Handler中去,給驗證作邏輯運算提供一些可靠的信息;我這裏是空的;本身根據自身狀況本身定義適當的屬性做爲初始數據的承載容器;
CommonAuthorizeHandler : AuthorizationHandler<CommonAuthorize>這個是重點,承載了驗證的邏輯運算
須要重寫override Task HandleRequirementAsync方法,全部的邏輯都在該方法中,他的主要邏輯和上面的自定義中間件很類似,只少了上面的第一步;驗證流程以下:
context.Succeed(requirement);是驗證成功,若是沒有這個,就默認驗證失敗
由於UserContext把負責了權限驗證,因此不會把流程搞得感受很亂,而且能夠重用,至於用那種形式驗證也很容易切換services.AddAuthorization(option => { #region 自定義驗證策略 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 { IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(TokenContext.securityKey))//拿到SecurityKey }; }); //自定義策略IOC添加 services.AddSingleton<IAuthorizationHandler, CommonAuthorizeHandler>();
以上代碼主要分3個部分
一、添加上面自定義的策略,並取名;
二、設置祕鑰,這個祕鑰就是上一節中生成jwt的祕鑰,必需要要同樣,不然是簽名不正確
三、注入上面創建的一個重要類CommonAuthorizeHandler,如上面代碼
到此爲止你就可使用自定義策略的驗證了;
從效果上看都是同樣的,稍微有點區別
至於你喜歡那種,就使用哪一種吧,性能能夠忽略不計;
無論使用哪一種方式使用jwt做爲身份和權限驗證是否是很簡單,關鍵這裏也把權限驗證的邏輯抽出來了,這樣代碼就更清晰明瞭了;
至於Authorize的屬性形式,還有不少其餘的策略,好比用戶、申明,角色等,可查看官網https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/?view=aspnetcore-2.0
下一章將講解 用戶,申明 ,角色的驗證,並這些怎麼在自定義的驗證中實現,以便你們對他有個清晰的對比