Spring Boot中使用token:jwt
token由3部分組成:Header,Payload,Signature。
其中Header記錄了簽名演算法和token 的型別。
Payload是以明文儲存的一些資訊,包括使用者自定義資訊。
Signature是使用簽名演算法,對Payload結合服務端才知道的私鑰進行簽名後得出的結果。
服務端對這3部分使用base64編碼,然後以.號分隔,就得到了token字串,格式為:
xxxxxx.yyyyyy.zzzzzz
每次前端進行請求時,帶上這個token字串。當服務端收到請求後,就會對Payload計算簽名,然後與token中的Signature進行比較,若一致,則通過。
邏輯簡單,完全可以自己寫一個token類來實現以上功能。
Spring Boot中提供了一個第三方的token庫,叫jwt。
- 在pom.xml中引入依賴:
<!-- JWT Json Web Token 依賴 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
- 生成token:
public String getAccessToken() {
JwtBuilder jwtBuilder = Jwts.builder();
long nowMillis = System.currentTimeMillis();
// 7個官方Payload欄位
jwtBuilder.setId("1.0"); // 編號/版本
jwtBuilder.setIssuer("ISSUER"); // 發行人
jwtBuilder.setSubject("SUBJECT"); // 主題
jwtBuilder.setAudience("AUDIENCE"); // 受眾
jwtBuilder.setIssuedAt(new Date(nowMillis)); // 簽發時間
jwtBuilder.setNotBefore(new Date(nowMillis)); // 生效時間
jwtBuilder.setExpiration(new Date(nowMillis + (60 * 60 * 1000))); // 失效時間
// 使用者自定義欄位
jwtBuilder.claim("id", "AXDBDCD");
jwtBuilder.claim("name", "testname");
jwtBuilder.claim("value", "123456");
// 定義私鑰
String HS256KEY = "xxxxxx";
// 簽名演算法
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 計算簽名key值
Key signingKey = new SecretKeySpec(Base64.decodeBase64(HS256KEY), signatureAlgorithm.getJcaName());
// 進行簽名
jwtBuilder.signWith(signatureAlgorithm, signingKey);
// 獲取token字串
String tokenString = jwtBuilder.compact();
return tokenString;
}
注意:
- 要先為jwtBuilder設定claims,然後再呼叫jwtBuilder的set介面來設定7個官方payload欄位。這是因為jwtBuilder是將7個payload欄位儲存在claims中的。若先設定了payload,再設定claims,會將已設定的payload覆蓋掉。
- payload欄位中:id通常設為版本號;issuer是發行人,通常設為公司名;subject是主題,通常設為工程名;audience是受眾,通常設為獲取token 的介面名。一般來說,audience多被設為” login”,也就是登入。
- 7個官方payload欄位中的3個時間:簽發時間(issued at),生效時間(not before),失效時間(expiration)。這3個時間使用的單位是秒。呼叫jwtBuilder的set介面來設定這3個時間時,只需要傳入Date()型資料即可,JwbBuilder會自動將其轉換為秒儲存在claims的相應欄位裡。詳見附錄。
- 解析token:
public boolean isTokenValid(String token) {
try {
Claims claims = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(token.trim()).getBody();
Date date = claims.getExpiration();
return new Date().before(date);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
將token字串解析為Jws物件,然後獲取claims,這樣就能拿到claims中儲存的各個欄位。然後自行判斷即可。其中解析用的signingKey與2中生成的signingKey是同一個。
由於(當前解析的時間<生效時間)或(當前解析的時間>失效時間)均會丟擲異常,所以需要對異常進行捕獲,返回false給前端,表示token已失效。
附錄
- claims的設定
JwtBuilder允許使用者自定義claims,並提供了3個相關函式:
JwtBuilder setClaims(Claims var1);
JwtBuilder setClaims(Map<String, Object> var1);
JwtBuilder claim(String var1, Object var2);
其中:
- 兩個setClaims會將之前已存在的claims覆蓋掉。
- claim會將新的值push給當前claims。
JwtBuilder將7個官方payload欄位也儲存在claims中。也就是說,使用者自定義的欄位和官方的payload欄位是存放在一起的。
因此,若已經為JwtBuilder設定了claims欄位,則不能再呼叫setClaims(),否則已設定的會被覆蓋。推薦使用claim()函式。
JwtBuilder提供了7個set函式來設定7個官方payload欄位。以setExpiration()為例。
·若還沒有呼叫JwtBuilder的setClaims(),則呼叫setExpiration(),會先為JwtBuilder新增一個空的claims,然後將expiration設定進去。
·若已經呼叫了JwtBuilder的setClaims(),則呼叫setExpiration(),會將expiration的值push到已存在的claims中。
- not before與expiration
7個官方payload欄位中,有3個時間:
·issue at:簽發時間
·not before:生效時間
·expiration:失效時間
當Jwts對token字串進行解析時,若存在not before和expiration,則會進行判斷:
當前解析的時間必須>=not before
當前解析的時間必須<=expiration
否則會丟擲異常。所以解析時需要對異常進行捕獲處理。
若這2個時間都沒有進行設定,則不會進行判斷。
然而,其流程是:
JwtBuilder呼叫setNotBefore()來設定時間→生成token返回給使用者→使用者請求→對請求的token進行解析。
其中:
- setNotBefore()來設定時間時,將時間轉換為秒,是一個long型的資料,儲存到claims中。
- 解析時,將claims中的秒取出來,轉成Date型資料,然後與當前時間的Date比較,來判斷是否丟擲異常。
因此:
jwtBuilder.setNotBefore(new Date());
與
jwtBuilder.claim("nbf", new Date().getTime()/1000);
效果是相同的。
其中7個欄位名(例如"nbf")定義在Claims介面中(例如Claims.NOT_BEFORE)。
相關文章
- Spring Boot使用JWT進行token驗證Spring BootJWT
- Spring Boot + Security + JWT 實現Token驗證+多Provider——登入系統Spring BootJWTIDE
- Spring Boot 3中將JWT與Spring Security 6整合Spring BootJWT
- JWT TokenJWT
- Composer 使用 JWT 生成 TOKEN 例項JWT
- spring boot中zookeeper使用Spring Boot
- spring boot中redis使用Spring BootRedis
- JWT 快速為使用者生成 tokenJWT
- 使用 JWT 身份驗證保護你的 Spring Boot 應用JWTSpring Boot
- JWT(Json WEB Token)JWTJSONWeb
- jwt生成token和token解析基礎JWT
- jwt生成token報錯JWT
- Spring Boot 最簡單整合 Shiro+JWT 方式Spring BootJWT
- 038.CI4框架CodeIgniter,使用Jwt生成token框架JWT
- 獲取jwt(json web token)中儲存的使用者資訊JWTJSONWeb
- springboot(十一):Spring boot中mongodb的使用Spring BootMongoDB
- Spring Boot中攔截器的使用Spring Boot
- Spring Boot中@Import三種使用方式!Spring BootImport
- SpringBoot 整合 JWT 實現 token 驗證,token 登出Spring BootJWT
- Spring Boot 2.0(四):使用 Docker 部署 Spring BootSpring BootDocker
- JSON Web Token(JWT) 簡介JSONWebJWT
- token 會話設計 (JWT)會話JWT
- 如何在SpringBoot中整合JWT(JSON Web Token)鑑權Spring BootJWTJSONWeb
- Spring Boot Admin 使用Spring Boot
- Spring Boot 使用1Spring Boot
- Spring Boot 整合 Sa-Token 實現登入認證Spring Boot
- Spring Boot(三):Spring Boot中的事件的使用 與Spring Boot啟動流程(Event 事件 和 Listeners監聽器)Spring Boot事件
- Spring Boot2中Swagger3使用Spring BootSwagger
- spring-boot-plus整合Shiro+JWT許可權管理SpringbootJWT
- 前後端分離之JWT(JSON Web Token)的使用後端JWTJSONWeb
- spring oauth2+JWT後端自動重新整理access_tokenSpringOAuthJWT後端
- 使用JWT的Spring Security - JakubLeškoJWTSpring
- jwt token 重新整理問題JWT
- asp.net core使用identity+jwt保護你的webapi(二)——獲取jwt tokenASP.NETIDEJWTWebAPI
- Spring Boot 2 + Spring Security 5 + JWT 的單頁應用Restful解決方案Spring BootJWTREST
- 使用Intellij中的Spring Initializr來快速構建Spring Boot工程IntelliJSpring Boot
- spring security oauth2搭建resource-server demo及token改造成JWT令牌SpringOAuthServerJWT
- 基於 JWT + Refresh Token 的使用者認證實踐JWT