JWT的说明和使用
JWT的说明和使用
JWT(JSON Web Token)是一种基于开放标准(RFC 7519),用于在网络应用间以JSON格式安全传递声明信息(如用户身份、权限等)。它通过数字签名保证信息的完整性和真实性,常被用于用户认证和信息传递,尤其适合前后端分离、分布式系统中的身份验证场景。
一、JWT的核心特点
1.自包含:Token本身包含用户身份等关键信息(无需频繁查询数据库)
2.无状态:服务器无需存储Token信息,减轻服务器负担(适合分布式系统)
3.跨域支持:基于JSON格式,可在不同语言、不同域之间传递
4.安全性:通过签名机制(HMAC、RSA等)防止信息被篡改
二、JWT的结构(三部分组成)
JWT由3个Base64编码的部分组成,用.分隔,格式为:header.payload.signature
1.Header(头部)
示例(JSON):
{ "alg":"HS256", "typ":"JWT"
}
作用:生命Token的类型(typ:"JWT")和签名算法(如HS256、RS256)
处理:JSON被Base64编码后作为第一部分,如:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2.Payload(载荷)
1. 作用:存储需要传递的声明信息(用户数据、过期时间等),分三种类型: 标准声明(推荐但不强制): iss:签发者exp:过期时间(时间戳,单位秒) sub:主题 aud:受众 iat:签发时间jti:Token唯一标识(用于防止重放攻击) 自定义声明:业务相关信息(如userId:123、role:"admin")
2. 注意:Payload仅经过Base64编码(未加密),不要存放敏感信息(如密码)
示例(JSON):
{"sub":"1234567890","name":"John Doe","iat":1516239022,"exp":1516242622,"userId":1001
}
处理:JSON被Base64编码后作为第二部分,如:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjIsInVzZXJJZCI6MTAwMX0
3.Signature(签名)
作用:对Header和Payload进行签名,防止数据被篡改 生成规则:1.用Header中指定的算法(如HS256)2.签名公式:
signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secretKey)
示例:用密钥secret对前两部分签名后,得到第三部分:
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c完整JWT示例,三部分拼接后:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjIsInVzZXJJZCI6MTAwMX0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
三、JWT工作流程(以用户登录为例)
1.用户登录:用户输入账号密码,发送登录请求到服务器
2.服务器验证:验证通过后,生成JWT(包含用户ID、过期时间等信息),并返回给客户端
3.客户端存储:客户端(浏览器/APP)将JWT存储在localStorage、sessionStorage或Cookie中
4.后续请求:客户端每次请求时,在HTTP头Authorization中携带JWT(格式:Bearer<token>)
5.服务器校验:服务器接收请求后,解析JWT并验证签名5.1 签名有效且未过期:解析Payload中的用户信息,处理请求5.2 前端无效或已过期:返回401(未授权),要求重新登录
四、Java实现JWT(基于jjwt库)
1.引入依赖(Maven)
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <version>0.11.5</version><scope>runtime</scope>
</dependency>
2.JWT工具类(生成、解析、验证)
import io.jsonwebtoken.*;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;public class JwtUtils {// 密钥(生产环境需放在配置文件,且足够复杂)private static final String SECRET_KEY = "your-256-bit-secret-key-which-should-be-very-long-and-secure";// 过期时间:1小时(单位毫秒)private static final long EXPIRATION_TIME = 3600000;// 生成 Tokenpublic static String generateToken(Long userId, String username) {// 自定义声明(可选)Map<String, Object> claims = new HashMap<>();claims.put("userId", userId);claims.put("username", username);return Jwts.builder().setClaims(claims) // 放入自定义声明.setIssuedAt(new Date()) // 签发时间.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // 过期时间.signWith(getSignInKey(), SignatureAlgorithm.HS256) // 签名算法和密钥.compact(); // 生成 Token}// 从 Token 中获取指定声明(如 userId)public static <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {final Claims claims = extractAllClaims(token);return claimsResolver.apply(claims);}// 解析 Token 获取所有声明private static Claims extractAllClaims(String token) {return Jwts.parserBuilder().setSigningKey(getSignInKey()) // 用密钥解析.build().parseClaimsJws(token).getBody();}// 验证 Token 是否有效(未过期且签名正确)public static boolean isTokenValid(String token) {try {// 解析时会自动校验签名和过期时间,若无效则抛出异常Jwts.parserBuilder().setSigningKey(getSignInKey()).build().parseClaimsJws(token);return true;} catch (JwtException | IllegalArgumentException e) {// 签名无效、过期、格式错误等都会触发异常return false;}}// 获取签名密钥(转换为 SecretKey 类型)private static SecretKey getSignInKey() {// 密钥需与签名算法匹配(HS256 要求密钥至少 256 位)byte[] keyBytes = SECRET_KEY.getBytes();return Keys.hmacShaKeyFor(keyBytes);}
}
3.使用示例(登录与验证)
// 1. 登录成功后生成 Token
@PostMapping("/login")
public R login(@RequestBody LoginDTO loginDTO) {// 验证账号密码(省略)User user = userService.verify(loginDTO.getUsername(), loginDTO.getPassword());// 生成 JWTString token = JwtUtils.generateToken(user.getId(), user.getUsername());return R.success(token);
}// 2. 接口访问时验证 Token(可通过拦截器实现)
@Component
public class JwtInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 从请求头获取 TokenString token = request.getHeader("Authorization");if (token == null || !token.startsWith("Bearer ")) {response.setStatus(401);return false;}token = token.replace("Bearer ", "");// 验证 Token 有效性if (!JwtUtils.isTokenValid(token)) {response.setStatus(401);return false;}// Token 有效,解析用户信息存入请求(供后续接口使用)Long userId = JwtUtils.extractClaim(token, claims -> claims.get("userId", Long.class));request.setAttribute("userId", userId);return true;}
}
五、JWT优缺点
优点
无状态:服务器无需存储Token,减轻分布式系统的会话共享压力
跨平台:基于JSON格式,支持多语言、多终端
自包含:减少数据库查询(用户信息直接从Token解析) 缺点
无法主动吊销:Token一旦生成,在过期前始终有效(除非服务器维护黑名单)
Payload未加密:Base64编码可被破解,不可存放敏感信息
过期时间固定:无法动态延长有效期(需客户端主动刷新Token)
六、使用建议
1.密钥安全:密钥必须保密且足够复杂(HS256推荐256位以上),避免硬编码
2.短期有效期:设置较短的过期时间(如1小时),并提供Token刷新机制
3.不存敏感信息:Payload仅放非敏感数据(如用户ID,角色)
4.HTTPS传输:防止Token在网络中被窃取
JWT是目前前后端分离架构中身份认证的主流方案,合理使用可大幅提升系统的灵活性和安全性
