JWT 高级特性深度解析
2025/9/17大约 10 分钟
JWT 高级特性深度解析
前置要求
在开始本教程之前,请确保您已经:
- 掌握了 JWT 的基础概念和实现
- 了解加密和签名的基础知识
- 熟悉 Java 加密 API
- 具备一定的安全知识背景
JWT 高级特性概述
JWT 不仅限于简单的身份验证,还提供了许多高级特性:
- 嵌套令牌 (Nested JWT):令牌中包含其他令牌
- JWE (JSON Web Encryption):加密的 JWT
- JWS (JSON Web Signature):签名的 JWT
- 自定义声明:扩展 JWT 功能
- 令牌链接:令牌间的关联关系
1. 嵌套令牌 (Nested JWT)
嵌套令牌允许在一个 JWT 中包含其他 JWT,这在微服务架构中特别有用。
嵌套令牌结构
外层令牌 (Outer JWT)
├── Header: 外层签名算法
├── Payload: 包含内层令牌
└── Signature: 外层签名
内层令牌 (Inner JWT)
├── Header: 内层签名算法
├── Payload: 实际数据
└── Signature: 内层签名
嵌套令牌实现
package com.example.jwt.advanced.nested;
import com.example.jwt.advanced.entity.User;
import com.example.jwt.advanced.entity.Permission;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
@RequiredArgsConstructor
@Slf4j
public class NestedJwtProvider {
private final String outerSecret = "outer-secret-key-must-be-at-least-256-bits-long";
private final String innerSecret = "inner-secret-key-must-be-at-least-256-bits-long";
/**
* 生成嵌套令牌
* 外层令牌包含用户基本信息,内层令牌包含详细权限
*/
public String generateNestedToken(User user, List<Permission> permissions) {
// 1. 生成内层令牌(包含详细权限信息)
String innerToken = generateInnerToken(user, permissions);
// 2. 生成外层令牌(包含内层令牌)
return generateOuterToken(user, innerToken);
}
/**
* 生成内层令牌
*/
private String generateInnerToken(User user, List<Permission> permissions) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
claims.put("username", user.getUsername());
claims.put("email", user.getEmail());
claims.put("permissions", permissions);
claims.put("tokenType", "INNER");
claims.put("issuedBy", "auth-service");
return Jwts.builder()
.setClaims(claims)
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时
.signWith(getInnerSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
/**
* 生成外层令牌
*/
private String generateOuterToken(User user, String innerToken) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
claims.put("username", user.getUsername());
claims.put("roles", user.getRoles());
claims.put("innerToken", innerToken); // 嵌套内层令牌
claims.put("tokenType", "OUTER");
claims.put("issuedBy", "gateway-service");
return Jwts.builder()
.setClaims(claims)
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 24小时
.signWith(getOuterSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
/**
* 解析嵌套令牌
*/
public NestedTokenInfo parseNestedToken(String nestedToken) {
try {
// 1. 解析外层令牌
Claims outerClaims = Jwts.parserBuilder()
.setSigningKey(getOuterSigningKey())
.build()
.parseClaimsJws(nestedToken)
.getBody();
// 2. 提取内层令牌
String innerToken = outerClaims.get("innerToken", String.class);
// 3. 解析内层令牌
Claims innerClaims = Jwts.parserBuilder()
.setSigningKey(getInnerSigningKey())
.build()
.parseClaimsJws(innerToken)
.getBody();
return NestedTokenInfo.builder()
.outerClaims(outerClaims)
.innerClaims(innerClaims)
.innerToken(innerToken)
.build();
} catch (JwtException e) {
log.error("嵌套令牌解析失败", e);
throw new RuntimeException("无效的嵌套令牌");
}
}
/**
* 验证嵌套令牌
*/
public boolean validateNestedToken(String nestedToken) {
try {
NestedTokenInfo tokenInfo = parseNestedToken(nestedToken);
// 检查外层令牌是否过期
Date outerExpiration = tokenInfo.getOuterClaims().getExpiration();
if (outerExpiration.before(new Date())) {
log.warn("外层令牌已过期");
return false;
}
// 检查内层令牌是否过期
Date innerExpiration = tokenInfo.getInnerClaims().getExpiration();
if (innerExpiration.before(new Date())) {
log.warn("内层令牌已过期");
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
/**
* 刷新内层令牌
*/
public String refreshInnerToken(String nestedToken, List<Permission> newPermissions) {
NestedTokenInfo tokenInfo = parseNestedToken(nestedToken);
// 获取用户信息
String username = tokenInfo.getOuterClaims().getSubject();
Long userId = tokenInfo.getOuterClaims().get("userId", Long.class);
// 创建用户对象
User user = new User();
user.setId(userId);
user.setUsername(username);
// 生成新的内层令牌
String newInnerToken = generateInnerToken(user, newPermissions);
// 生成新的外层令牌
return generateOuterToken(user, newInnerToken);
}
private SecretKey getOuterSigningKey() {
return Keys.hmacShaKeyFor(outerSecret.getBytes());
}
private SecretKey getInnerSigningKey() {
return Keys.hmacShaKeyFor(innerSecret.getBytes());
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class NestedTokenInfo {
private Claims outerClaims;
private Claims innerClaims;
private String innerToken;
}
2. JWE (JSON Web Encryption)
JWE 提供了 JWT 的加密功能,确保令牌内容不被未授权方读取。
JWE 结构
Header.EncryptedKey.IV.Ciphertext.Tag
JWE 实现
package com.example.jwt.advanced.encryption;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.AESEncrypter;
import com.nimbusds.jose.crypto.AESDecrypter;
import com.nimbusds.jwt.EncryptedJWT;
import com.nimbusds.jwt.JWTClaimsSet;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.SecureRandom;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
@Slf4j
public class JweTokenProvider {
private final SecretKey encryptionKey;
public JweTokenProvider() throws Exception {
// 生成加密密钥
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
this.encryptionKey = keyGen.generateKey();
}
/**
* 创建加密的 JWT
*/
public String createEncryptedToken(String subject, Map<String, Object> claims) {
try {
// 创建 JWT 声明集
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject(subject)
.issuer("secure-service")
.issueTime(new Date())
.expirationTime(new Date(System.currentTimeMillis() + 3600000))
.claim("customData", claims)
.build();
// 创建加密的 JWT
EncryptedJWT encryptedJWT = new EncryptedJWT(
new JWEHeader(JWEAlgorithm.A256GCMKW, EncryptionMethod.A256GCM),
claimsSet
);
// 加密
encryptedJWT.encrypt(new AESEncrypter(encryptionKey));
return encryptedJWT.serialize();
} catch (JOSEException e) {
log.error("创建加密 JWT 失败", e);
throw new RuntimeException("加密令牌创建失败");
}
}
/**
* 解密 JWT
*/
public JWTClaimsSet decryptToken(String encryptedToken) {
try {
// 解析加密的 JWT
EncryptedJWT encryptedJWT = EncryptedJWT.parse(encryptedToken);
// 解密
encryptedJWT.decrypt(new AESDecrypter(encryptionKey));
return encryptedJWT.getJWTClaimsSet();
} catch (Exception e) {
log.error("解密 JWT 失败", e);
throw new RuntimeException("令牌解密失败");
}
}
/**
* 验证加密令牌
*/
public boolean validateEncryptedToken(String encryptedToken) {
try {
JWTClaimsSet claimsSet = decryptToken(encryptedToken);
// 检查过期时间
Date expiration = claimsSet.getExpirationTime();
if (expiration != null && expiration.before(new Date())) {
log.warn("加密令牌已过期");
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
}
3. JWS (JSON Web Signature) 高级用法
多重签名 JWT
package com.example.jwt.advanced.signature;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Map;
@Component
@Slf4j
public class MultiSignatureJwtProvider {
private final SecretKey primaryKey;
private final SecretKey secondaryKey;
public MultiSignatureJwtProvider() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("HMACSHA256");
keyGen.init(256);
this.primaryKey = keyGen.generateKey();
this.secondaryKey = keyGen.generateKey();
}
/**
* 创建多重签名的 JWT
*/
public String createMultiSignedToken(String subject, Map<String, Object> claims) {
try {
// 创建 JWT 声明集
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject(subject)
.issuer("multi-sign-service")
.issueTime(new Date())
.expirationTime(new Date(System.currentTimeMillis() + 3600000))
.claim("data", claims)
.build();
// 创建签名的 JWT
SignedJWT signedJWT = new SignedJWT(
new JWSHeader(JWSAlgorithm.HS256),
claimsSet
);
// 使用主密钥签名
signedJWT.sign(new MACSigner(primaryKey));
return signedJWT.serialize();
} catch (JOSEException e) {
log.error("创建多重签名 JWT 失败", e);
throw new RuntimeException("多重签名令牌创建失败");
}
}
/**
* 验证多重签名 JWT
*/
public boolean validateMultiSignedToken(String token) {
try {
SignedJWT signedJWT = SignedJWT.parse(token);
// 尝试使用主密钥验证
boolean primaryValid = signedJWT.verify(new MACVerifier(primaryKey));
if (primaryValid) {
return validateClaims(signedJWT.getJWTClaimsSet());
}
// 尝试使用次密钥验证
boolean secondaryValid = signedJWT.verify(new MACVerifier(secondaryKey));
if (secondaryValid) {
return validateClaims(signedJWT.getJWTClaimsSet());
}
return false;
} catch (Exception e) {
log.error("验证多重签名 JWT 失败", e);
return false;
}
}
/**
* 验证声明
*/
private boolean validateClaims(JWTClaimsSet claimsSet) {
Date expiration = claimsSet.getExpirationTime();
return expiration != null && !expiration.before(new Date());
}
}
4. 自定义声明和扩展
自定义声明处理器
package com.example.jwt.advanced.claims;
import com.example.jwt.advanced.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
@RequiredArgsConstructor
@Slf4j
public class CustomClaimsProvider {
private final SecretKey signingKey;
/**
* 创建包含自定义声明的令牌
*/
public String createTokenWithCustomClaims(User user, CustomClaims customClaims) {
Map<String, Object> claims = new HashMap<>();
// 标准声明
claims.put("userId", user.getId());
claims.put("username", user.getUsername());
claims.put("email", user.getEmail());
claims.put("roles", user.getRoles());
// 自定义声明
claims.put("customClaims", customClaims);
claims.put("deviceInfo", customClaims.getDeviceInfo());
claims.put("location", customClaims.getLocation());
claims.put("sessionId", customClaims.getSessionId());
claims.put("permissions", customClaims.getPermissions());
return Jwts.builder()
.setClaims(claims)
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(signingKey, SignatureAlgorithm.HS256)
.compact();
}
/**
* 解析自定义声明
*/
public CustomClaims extractCustomClaims(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(signingKey)
.build()
.parseClaimsJws(token)
.getBody();
@SuppressWarnings("unchecked")
Map<String, Object> customClaimsMap = claims.get("customClaims", Map.class);
return CustomClaims.builder()
.deviceInfo(claims.get("deviceInfo", String.class))
.location(claims.get("location", String.class))
.sessionId(claims.get("sessionId", String.class))
.permissions(claims.get("permissions", java.util.List.class))
.build();
}
/**
* 更新自定义声明
*/
public String updateCustomClaims(String token, CustomClaims newClaims) {
Claims existingClaims = Jwts.parserBuilder()
.setSigningKey(signingKey)
.build()
.parseClaimsJws(token)
.getBody();
// 更新自定义声明
existingClaims.put("customClaims", newClaims);
existingClaims.put("deviceInfo", newClaims.getDeviceInfo());
existingClaims.put("location", newClaims.getLocation());
existingClaims.put("sessionId", newClaims.getSessionId());
existingClaims.put("permissions", newClaims.getPermissions());
return Jwts.builder()
.setClaims(existingClaims)
.signWith(signingKey, SignatureAlgorithm.HS256)
.compact();
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class CustomClaims {
private String deviceInfo;
private String location;
private String sessionId;
private java.util.List<String> permissions;
private Map<String, Object> additionalData;
}
5. 令牌链接和关联
令牌链接实现
package com.example.jwt.advanced.linking;
import com.example.jwt.advanced.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Component
@RequiredArgsConstructor
@Slf4j
public class TokenLinkingProvider {
private final SecretKey signingKey;
/**
* 创建令牌链接
*/
public TokenLink createTokenLink(User user, String linkType) {
String linkId = UUID.randomUUID().toString();
// 创建主令牌
String mainToken = createMainToken(user, linkId);
// 创建关联令牌
String linkedToken = createLinkedToken(user, linkId, linkType);
return TokenLink.builder()
.linkId(linkId)
.mainToken(mainToken)
.linkedToken(linkedToken)
.linkType(linkType)
.createdAt(new Date())
.build();
}
/**
* 创建主令牌
*/
private String createMainToken(User user, String linkId) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
claims.put("username", user.getUsername());
claims.put("linkId", linkId);
claims.put("tokenType", "MAIN");
claims.put("permissions", user.getRoles());
return Jwts.builder()
.setClaims(claims)
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 24小时
.signWith(signingKey, SignatureAlgorithm.HS256)
.compact();
}
/**
* 创建关联令牌
*/
private String createLinkedToken(User user, String linkId, String linkType) {
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
claims.put("linkId", linkId);
claims.put("tokenType", "LINKED");
claims.put("linkType", linkType);
// 根据链接类型设置不同的权限
switch (linkType) {
case "API_ACCESS":
claims.put("permissions", java.util.Arrays.asList("API_READ", "API_WRITE"));
break;
case "FILE_ACCESS":
claims.put("permissions", java.util.Arrays.asList("FILE_READ"));
break;
case "ADMIN_ACCESS":
claims.put("permissions", java.util.Arrays.asList("ADMIN_READ", "ADMIN_WRITE"));
break;
default:
claims.put("permissions", java.util.Arrays.asList("BASIC_ACCESS"));
}
return Jwts.builder()
.setClaims(claims)
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时
.signWith(signingKey, SignatureAlgorithm.HS256)
.compact();
}
/**
* 验证令牌链接
*/
public boolean validateTokenLink(String mainToken, String linkedToken) {
try {
Claims mainClaims = Jwts.parserBuilder()
.setSigningKey(signingKey)
.build()
.parseClaimsJws(mainToken)
.getBody();
Claims linkedClaims = Jwts.parserBuilder()
.setSigningKey(signingKey)
.build()
.parseClaimsJws(linkedToken)
.getBody();
// 检查链接ID是否匹配
String mainLinkId = mainClaims.get("linkId", String.class);
String linkedLinkId = linkedClaims.get("linkId", String.class);
if (!mainLinkId.equals(linkedLinkId)) {
log.warn("令牌链接ID不匹配");
return false;
}
// 检查令牌类型
String mainType = mainClaims.get("tokenType", String.class);
String linkedType = linkedClaims.get("tokenType", String.class);
if (!"MAIN".equals(mainType) || !"LINKED".equals(linkedType)) {
log.warn("令牌类型不正确");
return false;
}
// 检查过期时间
Date mainExpiration = mainClaims.getExpiration();
Date linkedExpiration = linkedClaims.getExpiration();
if (mainExpiration.before(new Date()) || linkedExpiration.before(new Date())) {
log.warn("令牌已过期");
return false;
}
return true;
} catch (Exception e) {
log.error("验证令牌链接失败", e);
return false;
}
}
/**
* 撤销令牌链接
*/
public void revokeTokenLink(String linkId) {
// 在实际应用中,这里应该将链接ID添加到黑名单
log.info("撤销令牌链接: {}", linkId);
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class TokenLink {
private String linkId;
private String mainToken;
private String linkedToken;
private String linkType;
private Date createdAt;
private Date revokedAt;
}
6. 高级令牌验证器
package com.example.jwt.advanced.validator;
import com.example.jwt.advanced.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Component
@RequiredArgsConstructor
@Slf4j
public class AdvancedTokenValidator {
private final SecretKey signingKey;
/**
* 高级令牌验证
*/
public ValidationResult validateToken(String token, ValidationContext context) {
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(signingKey)
.build()
.parseClaimsJws(token)
.getBody();
ValidationResult result = new ValidationResult();
// 基本验证
if (!validateBasicClaims(claims, result)) {
return result;
}
// 权限验证
if (!validatePermissions(claims, context, result)) {
return result;
}
// 上下文验证
if (!validateContext(claims, context, result)) {
return result;
}
// 自定义验证
if (!validateCustomRules(claims, context, result)) {
return result;
}
result.setValid(true);
result.setClaims(claims);
return result;
} catch (Exception e) {
log.error("令牌验证失败", e);
return ValidationResult.builder()
.valid(false)
.error("令牌解析失败: " + e.getMessage())
.build();
}
}
/**
* 验证基本声明
*/
private boolean validateBasicClaims(Claims claims, ValidationResult result) {
// 检查过期时间
Date expiration = claims.getExpiration();
if (expiration != null && expiration.before(new Date())) {
result.setError("令牌已过期");
return false;
}
// 检查发行时间
Date issuedAt = claims.getIssuedAt();
if (issuedAt != null && issuedAt.after(new Date())) {
result.setError("令牌发行时间无效");
return false;
}
// 检查主题
String subject = claims.getSubject();
if (subject == null || subject.isEmpty()) {
result.setError("令牌缺少主题");
return false;
}
return true;
}
/**
* 验证权限
*/
private boolean validatePermissions(Claims claims, ValidationContext context, ValidationResult result) {
@SuppressWarnings("unchecked")
List<String> tokenPermissions = claims.get("permissions", List.class);
if (tokenPermissions == null || tokenPermissions.isEmpty()) {
result.setError("令牌缺少权限信息");
return false;
}
// 检查所需权限
List<String> requiredPermissions = context.getRequiredPermissions();
if (requiredPermissions != null && !requiredPermissions.isEmpty()) {
for (String requiredPermission : requiredPermissions) {
if (!tokenPermissions.contains(requiredPermission)) {
result.setError("缺少所需权限: " + requiredPermission);
return false;
}
}
}
return true;
}
/**
* 验证上下文
*/
private boolean validateContext(Claims claims, ValidationContext context, ValidationResult result) {
// 检查设备信息
String tokenDeviceInfo = claims.get("deviceInfo", String.class);
String contextDeviceInfo = context.getDeviceInfo();
if (contextDeviceInfo != null && !contextDeviceInfo.equals(tokenDeviceInfo)) {
result.setError("设备信息不匹配");
return false;
}
// 检查位置信息
String tokenLocation = claims.get("location", String.class);
String contextLocation = context.getLocation();
if (contextLocation != null && !contextLocation.equals(tokenLocation)) {
result.setError("位置信息不匹配");
return false;
}
return true;
}
/**
* 验证自定义规则
*/
private boolean validateCustomRules(Claims claims, ValidationContext context, ValidationResult result) {
// 检查令牌类型
String tokenType = claims.get("tokenType", String.class);
String expectedType = context.getExpectedTokenType();
if (expectedType != null && !expectedType.equals(tokenType)) {
result.setError("令牌类型不匹配");
return false;
}
// 检查用户状态
Boolean enabled = claims.get("enabled", Boolean.class);
if (enabled != null && !enabled) {
result.setError("用户账户已禁用");
return false;
}
return true;
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class ValidationResult {
private boolean valid;
private String error;
private Claims claims;
private Map<String, Object> additionalInfo;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class ValidationContext {
private List<String> requiredPermissions;
private String deviceInfo;
private String location;
private String expectedTokenType;
private Map<String, Object> customContext;
}
使用示例
1. 嵌套令牌使用
@RestController
@RequestMapping("/api/advanced")
@RequiredArgsConstructor
public class AdvancedJwtController {
private final NestedJwtProvider nestedJwtProvider;
private final JweTokenProvider jweTokenProvider;
private final MultiSignatureJwtProvider multiSignatureProvider;
private final CustomClaimsProvider customClaimsProvider;
private final TokenLinkingProvider tokenLinkingProvider;
private final AdvancedTokenValidator tokenValidator;
/**
* 创建嵌套令牌
*/
@PostMapping("/nested-token")
public ResponseEntity<String> createNestedToken(@RequestBody User user) {
List<Permission> permissions = getPermissionsForUser(user);
String nestedToken = nestedJwtProvider.generateNestedToken(user, permissions);
return ResponseEntity.ok(nestedToken);
}
/**
* 创建加密令牌
*/
@PostMapping("/encrypted-token")
public ResponseEntity<String> createEncryptedToken(@RequestBody TokenRequest request) {
Map<String, Object> claims = new HashMap<>();
claims.put("sensitiveData", request.getSensitiveData());
String encryptedToken = jweTokenProvider.createEncryptedToken(
request.getSubject(), claims);
return ResponseEntity.ok(encryptedToken);
}
/**
* 创建令牌链接
*/
@PostMapping("/token-link")
public ResponseEntity<TokenLink> createTokenLink(@RequestBody LinkRequest request) {
TokenLink tokenLink = tokenLinkingProvider.createTokenLink(
request.getUser(), request.getLinkType());
return ResponseEntity.ok(tokenLink);
}
/**
* 高级令牌验证
*/
@PostMapping("/validate")
public ResponseEntity<ValidationResult> validateToken(
@RequestBody ValidationRequest request) {
ValidationContext context = ValidationContext.builder()
.requiredPermissions(request.getRequiredPermissions())
.deviceInfo(request.getDeviceInfo())
.location(request.getLocation())
.expectedTokenType(request.getTokenType())
.build();
ValidationResult result = tokenValidator.validateToken(request.getToken(), context);
return ResponseEntity.ok(result);
}
}
最佳实践
安全注意事项
- 密钥管理:使用密钥管理服务(KMS)管理密钥
- 令牌轮换:定期轮换签名密钥
- 最小权限:只包含必要的声明信息
- 加密传输:始终使用 HTTPS 传输令牌
性能优化
- 缓存验证结果:缓存已验证的令牌信息
- 异步验证:使用异步方式验证令牌
- 批量验证:批量验证多个令牌
- 压缩令牌:对大型令牌进行压缩
总结
本教程深入探讨了 JWT 的高级特性:
- ✅ 嵌套令牌:实现令牌的层次结构
- ✅ JWE 加密:保护敏感令牌内容
- ✅ 多重签名:增强令牌安全性
- ✅ 自定义声明:扩展令牌功能
- ✅ 令牌链接:实现令牌关联
- ✅ 高级验证:复杂的验证逻辑
下一步学习
- 学习 JWT 在区块链中的应用
- 了解 JWT 与 OAuth 2.0 的集成
- 探索 JWT 在物联网中的应用