JWT在身份驗證與信息交換中的實踐探索
JWT
JWT是指JSON Web Token,一種用于在網(wǎng)絡(luò)上安全傳輸信息的開放標準(RFC 7519),定義了一種緊湊的、自包含的方式,用于作為JSON對象在各方之間安全地傳輸信息。JWT由三部分組成,分別是頭部(Header)、載荷(Payload)和簽名(Signature)。通常被用來在用戶和服務(wù)器之間傳遞身份信息,以及在不同的系統(tǒng)之間安全地傳遞聲明。JWT通常被用于身份驗證和信息交換,特別是在前后端分離的應(yīng)用中。
JWT工作示意圖:
- 用戶登錄后,服務(wù)端會將用戶的識別信息進行加密生成一個有有效期的token返回給用戶端。
- 用戶端收到token后將其進行保存,并在以后的每一次請求時將該token帶上發(fā)送給服務(wù)器。
- 服務(wù)器接收到用戶的token后,進行驗證用戶的身份、權(quán)限、有效期等信息,驗證通過即放行,驗證不通過就拒絕服務(wù)。
JWT由三部分組成,分別是頭部(header)、載荷(payload)和簽名(signature)。
- 頭部(header):包含了令牌的類型(即JWT)和所使用的簽名算法(例如HMAC SHA256或RSA)。
- 載荷(payload):包含了要傳遞的信息,以及一些標準的聲明和自定義的聲明。標準的聲明包括令牌的過期時間(exp)、發(fā)布時間(iat)等。自定義的聲明可以根據(jù)需要添加。
- 簽名(signature):使用頭部指定的算法和密鑰對頭部和載荷進行簽名,以確保令牌在傳輸過程中沒有被篡改。
JWT的三部分使用點號(.)連接起來,形成一個完整的JWT令牌。例如:xxxxx.yyyyy.zzzzz
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJaSEFOR1hVTiIsImJvZHkiOnsidXNlclJvbGUiOiJhZG1pbiIsInVzZXJpZCI6IjAwMSJ9LCJleHAiOjE2NjI5NTIxNjIsImlhdCI6MTY2Mjk1MTU1NywianRpIjoiZGZhN2MyZjUtNGNjMC00OWFhLWFiMDUtYzZhY2M4M2YxMDViIn0.xOleM21i7-EI0oOq83Xm-nQVOufajHCupY2QjkpwreQ
JWT通常用于身份驗證和授權(quán),因為它們可以包含用戶的信息,并且可以被驗證和信任。通常用于API認證,因為可以在多個系統(tǒng)之間安全地傳遞信息,而無需使用cookie或session。
JWT使用
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
@Slf4j
@RestController
public class TokenController {
@Autowired
private TokenUtil tokenUtil;
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password){
if(!"admin".equals(username) || !"123".equals(password)){
log.info("賬號或者密碼錯誤!");
}
// 模擬從數(shù)據(jù)庫中獲取的用戶識別信息
String userId = "001";
String userRole = "admin";
Map<String,Object> dataMap = new HashMap<>();
dataMap.put("userId", userId);
dataMap.put("userRole", userRole);
// 將用戶識別信息存儲到token中
String token = tokenUtil.createToken(dataMap);
log.info("生成的token為:{}", token);
return token;
}
@PostMapping("/getUserInfo")
public String getUserInfo(@RequestParam String token){
if(ObjectUtils.isEmpty(token)){
log.info("token不能為空!");
return "token不能為空!";
}
log.info("收到的token為:{}", token);
Map<String, Object> dataMap = tokenUtil.parseToken(token);
String userRole = dataMap.get("userRole").toString();
if(!"admin".equals(userRole)){
log.info("非管理員角色,不允許訪問!");
return "非管理員角色,不允許訪問!";
}
String userId = dataMap.get("userId").toString();
return "允許登錄,用戶為:" + userId;
}
}
@Component
public class TokenUtil {
private static final String DEFAULT_SECRET = "666";
private static final String DEFAULT_DATA_KEY = "body";
private static final String DEFAULT_ISSUER = "REATHIN";
private static final Long DEFAULT_EXPIRE_TIME = 7*24*60*60L;
public String createToken(Map<String, Object> dataMap){
return createToken(dataMap, DEFAULT_SECRET);
}
public String createToken(Map<String, Object> dataMap, String secret){
// 指定使用的加密算法
Algorithm algorithm = Algorithm.HMAC256(secret);
return JWT.create()
.withClaim(DEFAULT_DATA_KEY, dataMap)
.withIssuer(DEFAULT_ISSUER)
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + DEFAULT_EXPIRE_TIME * 1000))
.withJWTId(UUID.randomUUID().toString())
.sign(algorithm);
}
public Map<String, Object> parseToken(String token){
return parseToken(token, DEFAULT_SECRET);
}
public Map<String, Object> parseToken(String token, String secret){
// 指定使用的加密算法
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
return decodedJWT.getClaim(DEFAULT_DATA_KEY).asMap();
}
}
JWT優(yōu)缺點
優(yōu)點:
- 無狀態(tài)性(Stateless):JWT包含了所有用戶的必要信息,服務(wù)器不需要存儲用戶的會話信息,因此可以輕松地擴展應(yīng)用程序。
- 跨域支持(Cross-Origin Support):JWT可以在跨域場景下使用,因為它可以通過HTTP頭部進行傳遞。
- 安全性(Security):JWT可以使用簽名和加密來驗證發(fā)送方的身份和確保數(shù)據(jù)的完整性。
- 靈活性(Flexibility):JWT可以用于各種場景,包括身份驗證和信息交換。
缺點:
- 無法撤銷(Non-Revocable):一旦JWT被簽發(fā),就無法撤銷,除非設(shè)置短期過期時間。
- 增加網(wǎng)絡(luò)負擔(Increased Network Overhead):由于JWT包含了用戶信息,因此會增加網(wǎng)絡(luò)傳輸?shù)呢摀?/li>
- 潛在安全風險(Potential Security Risks):如果JWT被盜取,攻擊者可以獲得用戶的所有信息,因此需要謹慎存儲和傳輸JWT。
- 不適合存儲敏感信息(Not Suitable for Storing Sensitive Information):由于JWT可以被解碼,因此不適合存儲敏感信息,如密碼等。