JWT
JWT(JSON Web Token)是一种开放标准,用于在各方之间安全地传输信息。相比 Session,JWT 更适合分布式系统和前后端分离架构。
JWT 结构
JWT 由三部分组成,用 . 分隔:
Header.Payload.SignatureHeader(头部)
{
"alg": "HS256",
"typ": "JWT"
}Base64Url 编码后作为第一部分。
Payload(负载)
包含声明(Claims):
{
"sub": "user123",
"username": "zhangsan",
"roles": ["ADMIN", "USER"],
"iat": 1699999999,
"exp": 1700003599
}| 声明 | 说明 |
|---|---|
iss | 签发者 |
sub | 主题(用户ID) |
exp | 过期时间 |
iat | 签发时间 |
Signature(签名)
防篡改,确保数据完整性:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret_key
)签名算法
| 算法族 | 算法 | 特点 |
|---|---|---|
| HMAC | HS256/HS384/HS512 | 对称加密,速度快 |
| RSA | RS256/RS384/RS512 | 非对称加密,安全性高 |
| EC | ES256/ES384/ES512 | 椭圆曲线,密钥短 |
JJWT 库使用
Maven 依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>生成 Token
import io.jsonwebtoken.Jwts;
SecretKey key = Jwts.SIG.HS256.key().build();
String token = Jwts.builder()
.subject("user123")
.issuer("my-app")
.claim("username", "zhangsan")
.claim("roles", Arrays.asList("ADMIN", "USER"))
.expiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(key, Jwts.SIG.HS256)
.compact();验证 Token
try {
var claims = Jwts.parser()
.verifyWith(key)
.build()
.parseSignedClaims(token)
.getPayload();
String userId = claims.getSubject();
} catch (JwtException e) {
// Token 无效
}Spring Boot 集成
方案一:Spring Security OAuth2 Resource Server(推荐)
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: https://auth.example.com/oauth2/default/v1/keys@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}
}方案二:自定义 JWT 过滤器
@Component
public class JWTFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String token = req.getHeader("Token");
if (token != null) {
return executeLogin(request, response);
}
return false;
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
String token = req.getHeader("Token");
JWTToken jwtToken = new JWTToken(token);
try {
getSubject(request, response).login(jwtToken);
return true;
} catch (Exception e) {
return false;
}
}
}JWT vs Session
| 特性 | JWT | Session |
|---|---|---|
| 存储位置 | 客户端 | 服务端 |
| 扩展性 | 好(无状态) | 差(需共享 Session) |
| 安全性 | 可加密 | 依赖 Cookie |
| 适用场景 | 分布式、微服务 | 单体应用 |
JWT 无状态的特性使其天然适合微服务架构,但也带来 Token 无法主动失效的问题,通常通过黑名单机制或短过期时间+Refresh Token 方案解决。与 Shiro 或 SpringSecurity 配合使用时,JWT 作为认证凭证传递,权限校验仍由框架完成。
常见问题处理
Token 过期
- 采用短过期时间(如 30 分钟)+ Refresh Token(如 7 天)方案
- Access Token 过期后,用 Refresh Token 换取新的 Access Token
- Refresh Token 也过期时,用户需重新登录
Token 被篡改
- 签名校验会自动失败,
JwtException被捕获 - 检查密钥是否泄露,必要时轮换密钥
- 使用非对称加密(RSA)时,确保公钥/私钥配对正确