JWT-JSON WEB TOKEN
Json web token (JWT), 是為了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標准((RFC 7519).該token被設計為緊湊且安全的,特別適用於分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也可以增加一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用於認證,也可被加密。
JWT組成
一個JWT由head,payload,signature三部分組成,使用
.進行分隔。例如:
ewogICd0eXAnOiAnSldUJywKICAnYWxnJzogJ0hTMjU2Jwp9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJhZG1pbiI6IHRydWUKfQ==.2eab8b1db04001b687461803d759ae849ad795cc9840de1795ccbe54bf4b43d7
header
header(頭部)一般有兩個部分:分別是typ和alg。
- typ:表示json的類型,即用來干什么的,在JWT中默認為"JWT"
- alg:表示加密的算法,如HS256。
一個標准的jwt header的json文本如下:
{
'typ': 'JWT',
'alg': 'HS256'
}
通過BASE64進行加密獲得一個字符串:ewogICd0eXAnOiAnSldUJywKICAnYWxnJzogJ0hTMjU2Jwp9
payload
payload(負載)用來保存需要傳遞的信息的一個json文本。不用來存儲敏感信息。
一個自定義的payload:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
通過BASE64進行加密獲得一個字符串:ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJhZG1pbiI6IHRydWUKfQ==
signature
signature(簽名)用來校驗當前的jwt是否經過篡改,用來校驗其有效性的。
signature是通過header和payload獲得的兩個BASE64加密后的字符串拼接后進行摘要后得到的一個字符串。
String secret = "secret"; //鹽,也就是保存在服務器的私鑰,用於校驗
String encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload);
String signature = HMACSHA256(encodedString, secret);
這樣我們就得到一個signature字符串:
2eab8b1db04001b687461803d759ae849ad795cc9840de1795ccbe54bf4b43d7
將這三者進行拼接,使用.拼接即可得到JWT。
ewogICd0eXAnOiAnSldUJywKICAnYWxnJzogJ0hTMjU2Jwp9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJhZG1pbiI6IHRydWUKfQ==.2eab8b1db04001b687461803d759ae849ad795cc9840de1795ccbe54bf4b43d7
JWT校驗
使用SHA256算法將
header.payload進行鹽值摘要,得到一個字符串signature。如果和jwt中的signature相同的話,那么表示當前的JWT是由我們下發的。這時,我們就可以根據BASE64進行解密獲取其中的數據來進行業務操作。
如果不同的話,那么表示該JWT不是我們下發的,停止之后的操作即可。
JWT獲取信息
對於前台獲取數據,我們可以直接將payload進行BASE64解密,因為我們相信該JWT來源,就算是被篡改的,也會被服務端進行校驗。
JJWT(java jwt api)使用
JJWT是一個供生成和解析jwt的java端工具。官方網址
導入依賴
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
測試代碼
User user = new User();
user.setId(1L);
user.setLogin("user");
user.setPassword("user");
user.setRole("ROLE_USER");
String secretKey = "MDk5ZmU2YzdhZWE5NWRhZTU0MjgzMTVmMTkxYTI5ZGJmODc3NWU2ZDc5OWI1YWMxZTE5NWYxZWVhY2VmZGYwMWQ1NmExNjI4M2M2OWUzOGM0Nzg1ZGU2YzgxNWVjYzNhODg4YzE0ODhlZDA0YjZlYTgzYzk3MGE4NWFkMmJmOGI=";
byte[] keyBytes = Decoders.BASE64.decode(secretKey);// 私鑰必須使用BASE64來獲取bytes
Key key = Keys.hmacShaKeyFor(keyBytes);// 創建自定義私鑰
String jws = Jwts.builder().setSubject("Joe").signWith(key).compact();// 官方示例
System.out.println(jws);
String compact = Jwts.builder()
.setSubject(user.getLogin())
.signWith(key,SignatureAlgorithm.HS512) // 默認使用HS256加密算法
.setExpiration(new Date(new Date().getTime() + 10000))
.compact();
System.out.println(compact);
//需要同時進行測試,否則會報解析異常。
Claims body = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(compact).getBody();
System.out.println(body);
String subject = body.getSubject();
System.out.println(subject);
得到結果:
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJKb2UifQ.Y3dJ9pJiIoq3Py4xe445ud6Mk02v-N0rBFa8v3zlwZycjoVXOObRGSuyyMw4vhbhwuGIEj4fdIbe9P68Y8tXaA
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiZXhwIjoxNjA2ODA2OTcxfQ.GAw3fSU3r6qq7tcPrjqlj4tVh0tGK-GqWGLfPUQfeVIAvRF2_1GEQySYgoRvVc24fw-AcjFO4UIcuWcrvZO_Rg
{sub=user, exp=1606806971}
user
其中header和body可以直接BASE64解密獲得數據。

