aspnet core 2.1中使用jwt从原理到精通二
在aspnet core中,自定義jwt管道驗證
有了上一節(jié)的內(nèi)容作為基礎(chǔ),那這點也是非常容易的,關(guān)鍵點在中間件,只是把上一級在測試類中的自定義驗證放到中間件中來即可,
不過需要注意:中間件 的位置很重要,只有它后面的管道才會收到影響;
那我們先建一個自定義中間件類:(中間件的詳細(xì)內(nèi)容這里就不講了,大家可以參考官網(wǎng)和其他博文)
/// <summary>
? ? /// 自定義授權(quán)中間件
? ? /// </summary>
? ? public class JwtCustomerAuthorizeMiddleware
? ? {
? ? ? ? private readonly RequestDelegate next;
? ? ? ? public JwtCustomerAuthorizeMiddleware(RequestDelegate next, string secret, List<string> anonymousPathList)
? ? ? ? {
? ? ? ? ? ? #region? ?設(shè)置自定義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 身份驗證,并設(shè)置用戶Ruser值
?
? ? ? ? ? ? var result = context.Request.Headers.TryGetValue("Authorization", out StringValues authStr);
? ? ? ? ? ? if (!result || string.IsNullOrEmpty(authStr.ToString()))
? ? ? ? ? ? {
? ? ? ? ? ? ? ? throw new UnauthorizedAccessException("未授權(quán)");
? ? ? ? ? ? }
? ? ? ? ? ? result = TokenContext.Validate(authStr.ToString().Substring("Bearer ".Length).Trim(), payLoad =>
? ? ? ? ? ? {
? ? ? ? ? ? ? ? var success = true;
? ? ? ? ? ? ? ? //可以添加一些自定義驗證,用法參照測試用例
? ? ? ? ? ? ? ? //驗證是否包含aud 并等于 roberAudience
? ? ? ? ? ? ? ? success = success && payLoad["aud"]?.ToString() == option.Audience;
? ? ? ? ? ? ? ? if (success)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? //設(shè)置Ruse值,把user信息放在payLoad中,(在獲取jwt的時候把當(dāng)前用戶存放在payLoad的ruser鍵中)
? ? ? ? ? ? ? ? ? ? //如果用戶信息比較多,建議放在緩存中,payLoad中存放緩存的Key值
? ? ? ? ? ? ? ? ? ? userContext.TryInit(payLoad["ruser"]?.ToString());
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return success;
? ? ? ? ? ? });
? ? ? ? ? ? if (!result)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? throw new UnauthorizedAccessException("未授權(quán)");
? ? ? ? ? ? }
? ? ? ? ? ? #endregion
? ? ? ? ? ? #region 權(quán)限驗證
? ? ? ? ? ? if (!userContext.Authorize(context.Request.Path))
? ? ? ? ? ? {
? ? ? ? ? ? ? ? throw new UnauthorizedAccessException("未授權(quán)");
? ? ? ? ? ? }
? ? ? ? ? ? #endregion
? ? ? ? ? ? await next(context);
? ? ? ? }
? ? }
上面這個中間件中有個UserContext上線文,這個類主要管理當(dāng)前用戶信息和權(quán)限,其他信息暫時不管;我們先看一下這個中間件的驗證流程如何:
該中間件主要是針對訪問的路徑進(jìn)行驗證,當(dāng)然你也可以針對其他信息進(jìn)行驗證,比如(控制器名稱,動作名稱,等)
檢查當(dāng)前url是否可以匿名訪問,如果可以就直接通過,不做驗證了;如果不是可以匿名訪問的路徑,那就繼續(xù)
獲取當(dāng)前http頭部攜帶的jwt(存放在頭部的 Authorization中);
使用上一節(jié)的講的TokenContext做必須的驗證和自定義復(fù)雜驗證;
獲取當(dāng)前訪問用戶信息,我們把用戶的基本信息放在payLoad["ruser"]中,請看代碼如何操作
到這里為止,都是做的身份驗證,表明你是一個有身份的的人;接下來是做權(quán)限驗證,你是一個有身份的人,并不代表你是一個隨便到處訪問的人;你能訪問哪些url或者action,就要得到權(quán)限驗證的認(rèn)可
我們把權(quán)限驗證放到 userContext.Authorize方法中(這里怎么操作,這里就不深入講解,基本原理是從數(shù)據(jù)庫或者緩存中獲取當(dāng)前用戶對應(yīng)的權(quán)限列表,也就是url列表,進(jìn)行對比);
自定義中間件使用jwt驗證就這些內(nèi)容,是不是感覺很清晰,很簡單,有木有;
中間已經(jīng)完成了,那接下來我們來使用它,我們再startup中的Configure方法中添加如下代碼
app.UseMiddleware<JwtCustomerAuthorizeMiddleware>(Configuration["JwtOption:SecurityKey"], new List<string>() { "/api/values/getjwt","/" });
當(dāng)然上面可匿名訪問的url也可以定義在appsetting.json文件中,可以自行嘗試
如何通過自定義策略形式實現(xiàn)自定義jwt驗證
創(chuàng)建自定義策略的詳細(xì)介紹可以參考官網(wǎng),這里就不詳細(xì)介紹,
首先我們上代碼,創(chuàng)建自定義策略非常重要的兩個類,如下:
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 身份驗證,并設(shè)置用戶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)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? //設(shè)置Ruse值,把user信息放在payLoad中,(在獲取jwt的時候把當(dāng)前用戶存放在payLoad的ruser鍵中)
? ? ? ? ? ? ? ? ? ? //如果用戶信息比較多,建議放在緩存中,payLoad中存放緩存的Key值
? ? ? ? ? ? ? ? ? ? userContext.TryInit(payLoad["ruser"]?.ToString());
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return success;
? ? ? ? ? ? });
? ? ? ? ? ? if (!result)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? return Task.CompletedTask;
? ? ? ? ? ? }
? ? ? ? ? ? #endregion
? ? ? ? ? ? #region 權(quán)限驗證
? ? ? ? ? ? if (!userContext.Authorize(httpContext.Request.Path))
? ? ? ? ? ? {
? ? ? ? ? ? ? ? return Task.CompletedTask;
? ? ? ? ? ? }
? ? ? ? ? ? #endregion
? ? ? ? ? ? context.Succeed(requirement);
? ? ? ? ? ? return Task.CompletedTask;
? ? ? ? }
? ? }
? ? public class CommonAuthorize: IAuthorizationRequirement
? ? {
? ? }
其中兩個重要的類是哪兩個呢?他們的作用又是什么呢?
需要重寫override Task HandleRequirementAsync方法,所有的邏輯都在該方法中,他的主要邏輯和上面的自定義中間件很相似,只少了上面的第一步;驗證流程如下:
獲取當(dāng)前http頭部攜帶的jwt(存放在頭部的 Authorization中);
使用上一節(jié)的講的TokenContext做必須的驗證和自定義復(fù)雜驗證;
獲取當(dāng)前訪問用戶信息,我們把用戶的基本信息放在payLoad["ruser"中,請看代碼如何操作
到這里為止,都是做的身份驗證,表明你是一個有身份的的人;接下來是做權(quán)限驗證,你是一個有身份的人,并不代表你是一個隨便到處訪問的人;你能訪問哪些url或者action,就要得到權(quán)限驗證的認(rèn)可
我們把權(quán)限驗證放到 userContext.Authorize方法中(這里怎么操作,這里就不深入講解,基本原理是從數(shù)據(jù)庫或者緩存中獲取當(dāng)前用戶對應(yīng)的權(quán)限列表,也就是url列表,進(jìn)行對比);
因為UserContext把負(fù)責(zé)了權(quán)限驗證,所以不會把流程搞得感覺很亂,并且可以重用,至于用那種形式驗證也很容易切換
是不是很簡單,和自定義管道驗證的的代碼幾乎一模一樣,
如何使用自定義定義策略呢?
在startup類中的ConfigureServices中加入如下代碼:
以上代碼主要分3個部分
1、添加上面自定義的策略,并取名;
2、設(shè)置秘鑰,這個秘鑰就是上一節(jié)中生成jwt的秘鑰,必須要要一樣,否則是簽名不正確
3、注入上面建立的一個重要類CommonAuthorizeHandler,如上面代碼
在startup類中的Configure中添加?app.UseAuthentication();
在需要驗證的Controller或者Action中加上[Authorize(Policy = "common")]屬性,看下圖:
到此為止你就可以使用自定義策略的驗證了;
?
使用管道和自定義策略兩種形式進(jìn)行驗證有什么區(qū)別呢?
從效果上看都是一樣的,稍微有點區(qū)別
使用管道的方式,感覺方便點,清晰點
使用自定義策略的方式,效率稍微高一點,畢竟不是所有的請求都會進(jìn)行是否可以匿名訪問運算和建立管道的消耗,只有加入Authorize屬性的Controller和Action的才會進(jìn)入;當(dāng)然這點損耗可以忽略不計,看自己的喜好;
至于你喜歡那種,就使用哪種吧,性能可以忽略不計;
不管使用哪種方式使用jwt作為身份和權(quán)限驗證是不是很簡單,關(guān)鍵這里也把權(quán)限驗證的邏輯抽出來了,這樣代碼就更清晰明了了;
至于Authorize的屬性形式,還有很多其他的策略,比如用戶、申明,角色等,可查看官網(wǎng)https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/?view=aspnetcore-2.0
下一章將講解 用戶,申明 ,角色的驗證,并這些怎么在自定義的驗證中實現(xiàn),以便大家對他有個清晰的對比
相關(guān)文章:
aspnet core 2.1中使用jwt從原理到精通一
IdentityServer4之JWT簽名(RSA加密證書)及驗簽
IdentityServer4實戰(zhàn) - 談?wù)?JWT Token 的安全策略
如何簡單的在 ASP.NET Core 中集成 JWT 認(rèn)證?
原文地址:?https://www.cnblogs.com/lechengbo/p/9861501.html
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的aspnet core 2.1中使用jwt从原理到精通二的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [翻译] 初看 ASP.NET Core
- 下一篇: CentOS开发ASP.NET Core