C# 實現Jwtbearer Authentication


Jwtbearer Authentication

什么是JWT

JWT(JSON Web Token), 顧名思義就是在Web上以JSON格式傳輸的Token(RFC 7519)。

該Token被設計為緊湊聲明表示格式,特別適用於分布式站點的單點登錄(SSO)場景。

緊湊 :意味着size小,所以可以在URL中,Header中,Post Parameter中進行傳輸,並且包含了所需要的信息。

JWT的構成

JWT一般由三段構成,用"."號分隔開

Header.Payload.Signature

例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

點擊鏈接 如下圖

圖左邊為Header.Payload.Signature的base64編碼

圖右構成

Header

  • alg:聲明加密的算法 ,這里為HS256
  • typ:聲明類型,這里為JWT

然后將Header進行base64編碼 得到第一部分

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

Payload

由三部分構成

  • 標准中注冊的聲明

  • 公共的聲明

  • 私有的聲明

    標准中注冊的聲明 (建議但不強制使用) :

    • iss: jwt簽發者

    • sub: jwt所面向的用戶

    • aud: 接收jwt的一方

    • exp: jwt的過期時間,這個過期時間必須要大於簽發時間

    • nbf: 定義在什么時間之前,該jwt都是不可用的.

    • iat: jwt的簽發時間

    • jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。

    公共的聲明 :

公共的聲明可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密.

私有的聲明 :

私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因為base6編碼可以歸類為明文信息 。

定義一個payload:

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

然后將其進行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

Signature(數字簽名,防止信息被篡改)

jwt的第三部分是一個簽證信息,這個簽證信息由三部分組成:

  • Header (base64后的)

  • Payload (base64后的)

  • Secret

    這個部分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串,然后通過header中聲明的加密方式進行加鹽secret組合加密,然后就構成了jwt的第三部分 。

    // javascript 
    var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);  
    var signature = HMACSHA256(encodedString, 'secret');
    

    將這三部分用.連接成一個完整的字符串,構成了最終的jwt:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

    注意:secret是保存在服務器端的,jwt的簽發生成也是在服務器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味着客戶端是可以自我簽發jwt了。

下面我們自己來實現一下 JwtBearer Authentication

  1. 新建一個WebApi項目

  1. 新建JwtSeetings類

        public class JwtSeetings
        {
            /// <summary>
            /// 誰頒發的jwt
            /// </summary>
            public string Issuer { get; set; }
    
            /// <summary>
            /// 誰使用這個jwt
            /// </summary>
            public string Audience { get; set; }
    
            /// <summary>
            /// secret是保存在服務器端的,jwt的簽發生成也是在服務器端的,secret就是用來進行jwt的簽發和jwt的驗證,
            /// 所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味着客戶端是可以自我簽發jwt了
            /// 通過jwt header中聲明的加密方式進行加鹽secret組合加密,然后就構成了jwt的第三部分
            /// </summary>
            public string SecretKey { get; set; }
        }
    
  2. appsettings.json里面配置如下

    "JwtSeetings": {
        "Issuer": "http://localhost:5000",
        "Audience": "http://localhost:5000",
        "SecretKey": "zhoudafu201807041123"
    
  3. Startup類里面ConfigureServices添加如下代碼

        services.Configure<JwtSeetings>(Configuration.GetSection("JwtSeetings"));
    
                var jwtSeetings = new JwtSeetings();
                //綁定jwtSeetings
                Configuration.Bind("JwtSeetings", jwtSeetings);
                services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    
                })
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidIssuer = jwtSeetings.Issuer,
                        ValidAudience = jwtSeetings.Audience,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSeetings.SecretKey))
                    };
                })
                ;
    
  4. Startup類里面Configure添加如下代碼

     app.UseAuthentication();
    
  5. 新增AuthroizeController控制器,並添加如下代碼

    [HttpPost]
            public ActionResult Post([FromBody]LoginViewModel loginViewModel)
            {
    
                if (!ModelState.IsValid)
                {
                    return BadRequest();
                }
                if (loginViewModel.Name == "jack" && loginViewModel.Password == "rose")
                {
    
                    var claims = new Claim[]
                    {
                        new Claim(ClaimTypes.Name,"jack"),
                        new Claim(ClaimTypes.Role,"admin")
                    };
                    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSeetings.SecretKey));
                    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
                    var token = new JwtSecurityToken(
                        _jwtSeetings.Issuer,
                        _jwtSeetings.Audience,
                        claims,
                        DateTime.Now,
                        DateTime.Now.AddMinutes(30),
                        creds
                        );
                    return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
                }
    
                return BadRequest();
            }
    
  6. 給ValuesController控制器打上[Authorize]特性

  7. 用Postman直接訪問http://localhost:5000/api/Values 返回401

    1530672358385

  8. 用Postman訪問http://localhost:5000/api/Authroize 得到Token

    1530672442132

  9. 通過Bearer訪問成功

1530672572449

源代碼 https://github.com/HisKingdom/JwtAuthSample

參考博客:https://www.jianshu.com/p/576dbf44b2ae


免責聲明!

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



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