aspnet core 2.1中使用jwt從原理到精通三


目錄

  1. 如何使用角色,用戶,策略,等進行驗證
  2. jwt自定義邏輯驗證和上面原生的驗證對比

學有所得

  1. 使用jwt自定義完整自己任何想要的嚴謹認證
  2. 了解原生驗證的本質

如何使用角色,用戶,策略,等進行驗證

經過前篇的介紹,大家對jwt應該有很清晰的認識了,接下來我們如何讓代碼更清晰,以便以后面的擴展和對比;

首先,我們先把一篇的自定義策略驗證進行封裝,放到一個靜態擴展方法中去,和上一節還是有點區別,多加了一點東西,如下:

    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; }
    }

第一、在以前的基礎上添加了幾個簡單的自定義策略(onlyRober,onlyAdminOrSuperUser,multiClaim),

  1. onlyRober:jwt的申明部分有name=rober就能通過,也就是payLoad["name"]="rober"
  2. onlyAdminOrSuperUser:只有Admin和SuperUser角色的用戶才能通過,實質上就是payLoad["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]="Admin"或者“SuperUser”;
  3. multiClaim:多申明同時滿足,實質上也就是payLoad["aud"]=“roberAudience”(通過config["JwtOption:Audience"]獲取),或者,有name這個申明項,payLoad["name"],不管它的值是多少,當然這里的或者,也可以改為並且,把||改為&&,請仔細看上面的驗證diamante

第二、還有個自定義策略AgeRequireMent,這個和上一篇講的類似,也是兩個重要的類組成(AgeRequireHandler 和 AgeRequireMent )請自行參考代碼,如下:

 /// <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;
    }
View Code

 

第三、AddJwtBearer方法中添加了驗證簽名正確的驗證項目

ValidateIssuer = true,//是否驗證Issuer,設置值為 ValidIssuer = config["JwtOption:Issuer"],//Issuer,這兩項和前面簽發jwt的設置一致
ValidateAudience = true,//是否驗證Audience ,設置值 ValidAudience = config["JwtOption:Audience"],//
ValidateLifetime = true,//是否驗證失效時間
ValidateIssuerSigningKey = true,//是否驗證SecurityKey

第四,把自定義策略,注入進去

其次,我們如何使用上面定義了那么多策略呢?通過[Authorize(Policy = "策略名稱")]放在controller或者action上面

代碼如下:

/// <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);
        }

    }
View Code

看到這里使用內置的AuthorizeAttribute類進行各種策略驗證是不是也很簡單呢?

估計大家對各種策略的實質也有深刻認識了,接下來我們來做個對比,實際上它內部也就是那樣的實現的

jwt自定義邏輯驗證和上面原生的驗證對比

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 =>
{
return context.User.HasClaim("aud", "roberAudience") || context.User.HasClaim(c => c.Value =="name");
}));

     
         

 

 上面都是等價的,其他類推,無非就是正對payLoad的值進行各種對比;

到這里jwt的驗證也接近尾聲了,是不是覺得jwt驗證很簡單啊?

不要被各種寫法給繞暈了,仔細看看它的原理,其實非常簡單,無非就是對payload的各種值進行判斷和jwt的有效性驗證;

在項目中,其實用的最多的就是上一節的內自定義jwt管道驗證和自定義策略“common”的驗證方法,我很多項目中就是這么用的,至於用戶,角色策略用的非常少;

接下來,我將發表一個aspnet core 2.1以上版本的后端架構方面的博文,這個架構可是實戰中的架構(已經經歷20多個項目,10多年的不斷更新和刪減),不是demo;

希望通過簡單易懂的原理入門,到深入了解,再到輕松愉快運用的過程,最終希望大家共同進步,共同加入,開源出去;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM