.net core中使用jwt進行認證

world發表於2020-09-06

JSON Web Token(JWT)是一個開放標準(RFC 7519),它定義了一種緊湊且自包含的方式,用於在各方之間作為JSON物件安全地傳輸資訊。由於此資訊是經過數字簽名的,因此可以被驗證和信任。可以使用祕密(使用HMAC演算法)或使用RSAECDSA的公用/專用金鑰對對JWT進行簽名

傳統token

當用登入成功後,服務端會根據使用者的資訊生成一個token,然後將token儲存到redis中。當使用者再次訪問時會攜帶這個token訪問,這時服務端會先去查一下redis,看有沒有這個token或者是檢視這個token是否過期,當redis中沒有這個token時,登入驗證失敗,提示使用者登入失敗,否則直接放行。當然傳統的token也有其一定的優勢,比如說返回token能遮蔽使用者的真實資訊,臨時且唯一。但當併發數量大的時候,這種模式存在的問題就是,使用者一訪問就去查redis,會增加redis的壓力。

JWT

jwt通常由三部分組成分別是:

  • Header
  • Payload
  • Signature

  Header通常由兩部分組成,令牌的型別(即JWT)和所使用的簽名演算法,例如HMAC SHA256或RSA。

{
  "alg": "HS256",
  "typ": "JWT"
}

  Payload 令牌的第二部分是有效負載,其中包含宣告。宣告是有關實體(通常是使用者)和其他資料的宣告。有以下三種型別:註冊宣告,公共宣告和私人宣告。

  標準註冊宣告  

  iss:jwt的發行方
  sub:jwt宣告的主題
  aud:jwt所面向的群體
  exp:到期時間
  nbf:(不早於)宣告標識了JWT之前的時間不得接受處理

  公共宣告

    使用JWT的人可以隨意定義這些宣告。主要包括使用者的各種資訊但要避免私密的資訊

  私密宣告

    私有宣告是釋出者和麵曏者所共同定義的宣告

{
  "phone": "1234567890",
  "name": "John Doe",
  "admin": true
}

  Signature

signature是一個簽名資訊,是對前兩部分進行base64加密和secret一起進行組合加密,這裡的secret就相當於一個加鹽的操作

例如,如果要使用HMAC SHA256演算法,則將通過以下方式建立簽名:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

最後生成的token就是下面的這種格式

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoid293byIsInN1YiI6InN1Yk5hbWUiLCJuYmYiOiIxNTk5MzY3NjUwIiwiZXhwIjoxNTk5MzY3OTUwLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQ5OTk5IiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo0OTk5OSJ9.cqn55T-VFKkKuG2hSdQ_WNqjBhYiK9o3LvK-E9a893Y

通過jwt的工作原理,我們會發現jwt與傳統的token相比,jwt不用去redis中查詢對應的token資訊而是通過定義的加密演算法去進行校驗,這一塊算是對token的重大改進吧

.Net Core中使用JWT

1.通過nuget安裝 Microsoft.AspNetCore.Authentication.JwtBearer

2.在ConfigureServices中進行相應的註冊

      //使用jwt進行認證
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options => {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,  //是否驗證超時  當設定exp和nbf時有效 
                        ValidateIssuerSigningKey = true,  ////是否驗證金鑰
                        ValidAudience = "http://localhost:49999",//Audience
                        ValidIssuer = "http://localhost:49998",//Issuer,這兩項和登陸時頒發的一致
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("123456888jdijxhelloworldprefect")),     //拿到SecurityKey
                        //緩衝過期時間,總的有效時間等於這個時間加上jwt的過期時間,如果不配置,預設是5分鐘                                                                                                            //注意這是緩衝過期時間,總的有效時間等於這個時間加上jwt的過期時間,如果不配置,預設是5分鐘
                        ClockSkew = TimeSpan.FromMinutes(5)   //設定過期時間
                    };
                });

2.在Configure中新增認證授權中介軟體

  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseRouting();
            app.UseAuthentication();   //認證
            app.UseAuthorization();    //授權
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

3.登入成功後生成token並返回客戶端

       [HttpPost]
        public IActionResult Login(string username,string password)
        {
            var user = bll.GetUser(username, password);
            if (user != null)
            {
                var claims = new[]
               {
                   new Claim(ClaimTypes.Name,username),
                   new Claim(JwtRegisteredClaimNames.Sub, "subName"),
                   new Claim(JwtRegisteredClaimNames.Nbf, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),    //NotBefore  token生效時間
                   new Claim(JwtRegisteredClaimNames.Exp, $"{new DateTimeOffset(DateTime.Now.AddMilliseconds(1)).ToUnixTimeSeconds()}") //Expiration  到期時間,按秒數計算
                };
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("123456888jdijxhelloworldprefect"));   //key的長度要超過16個字元,不然回丟擲異常
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                var token = new JwtSecurityToken(
                   issuer: "http://localhost:49999",           //頒發token的web應用程式
                   audience: "http://localhost:49998",
                   claims: claims,
                   expires: DateTime.Now.AddMinutes(5),
                   signingCredentials: creds);
                return Ok(new
                {
                    token = new JwtSecurityTokenHandler().WriteToken(token),success=true,message="登入成功"
                });
            }
            else
            {
                return BadRequest(new { success = false, message = "登入失敗,請重試" });
            }
        }
用postman進行測試
1.呼叫登入介面生成token

 

 2.不加token去訪問保護的資源

 

 3.帶上生成的token再次訪問

 

 

 

 

相關文章