JWT(JSON Web Token)就是一种“登录后发给客户端的身份凭证”,本质是一个字符串。后续每次请求客户端都带上它,服务端验证通过就认为“你已登录”。
1)JWT 是什么,有什么用
-
解决问题:HTTP 无状态,服务端无法知道你之前是否登录
-
做法:登录成功 → 服务端生成 JWT → 返回给前端 → 前端存起来(localStorage/APP存储)→ 之后请求都带
token -
优点:
- 跨域/跨端(PC、移动端)都好用
- 支持集群(不依赖服务器内存 Session)
- 服务端压力小(不需要保存会话数据)
2)JWT 的结构(必须记住)
JWT = Header.Payload.Signature(三段用 . 分割)
Header(头)
描述令牌类型、算法,比如:
{"alg":"HS256","typ":"JWT"}
Payload(载荷)
你放的数据(声明 claims),比如 id、username,以及系统字段 exp(过期时间)等。
注意:Payload 只是 Base64 编码,不是加密,别放密码/敏感信息。
Signature(签名)
用 密钥 + header + payload 按算法算出来的签名
- 作用:防篡改
- 任何一位改了,验签就失败
✅ 口诀:payload可看不可改,signature保证不被改
3)JWT 实现要做哪两件事
A. 生成 token(登录成功时)
- 查询用户名密码正确
- 组装 claims(比如 id/username)
- 设置过期时间
- 使用密钥签名 → 生成字符串 token
B. 校验 token(每次请求时)
-
从请求头拿 token(比如 header:
token或标准Authorization: Bearer xxx) -
解析 token:
- 能解析:合法(未过期、未篡改、密钥正确)
- 解析报错:非法(过期/篡改/密钥不对)→ 返回 401
4)SpringBoot + jjwt 的典型实现
4.1 引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
4.2 JWT 工具类(生成 + 解析)
public class JwtUtils {
private static String signKey = "SVRIRUlNQQ=="; // 签名密钥(自己换)
private static Long expire = 12 * 3600 * 1000L; // 12小时
// 生成JWT
public static String generateJwt(Map<String,Object> claims){
return Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, signKey)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
}
// 解析JWT(校验:签名+过期)
public static Claims parseJWT(String jwt){
return Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
}
}
4.3 登录成功后下发 token
public LoginInfo login(Emp emp) {
Emp empLogin = empMapper.getUsernameAndPassword(emp);
if(empLogin == null) return null;
Map<String,Object> claims = new HashMap<>();
claims.put("id", empLogin.getId());
claims.put("username", empLogin.getUsername());
String token = JwtUtils.generateJwt(claims);
return new LoginInfo(empLogin.getId(), empLogin.getUsername(), empLogin.getName(), token);
}
4.4 统一拦截校验 token(拦截器示例)
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
String token = request.getHeader("token");
if(token == null || token.isEmpty()){
response.setStatus(401);
return false;
}
try {
JwtUtils.parseJWT(token); // 能解析就代表合法
return true;
} catch (Exception e) {
response.setStatus(401);
return false;
}
}
}
注册拦截器(放行登录接口):
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired private TokenInterceptor tokenInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
}
5)容易踩的坑(高频)
- 密钥不一致:生成用的 key 和解析用的 key 必须一样
- token 放错位置:后端从 header 取,前端要在请求头带上
- payload 放敏感信息:JWT 不是加密,别放密码/身份证等
- 过期处理:过期会解析失败,要返回 401,让前端跳登录
- 推荐标准请求头:
Authorization: Bearer <token>(更规范)