本文轉自:https://www.cnblogs.com/aishangyipiyema/p/9262642.html
什么是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
- 新建一個WebApi項目
新建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; } }
appsettings.json里面配置如下
"JwtSeetings": { "Issuer": "http://localhost:5000", "Audience": "http://localhost:5000", "SecretKey": "zhoudafu201807041123"
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)) }; }) ;
Startup類里面Configure添加如下代碼
app.UseAuthentication();
新增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(); }
給ValuesController控制器打上[Authorize]特性
用Postman直接訪問http://localhost:5000/api/Values 返回401
用Postman訪問http://localhost:5000/api/Authroize 得到Token
通過Bearer訪問成功
參考博客:https://www.jianshu.com/p/576dbf44b2ae