一、JWT令牌详解:新一代无状态身份验证方案
JWT(JSON Web Token) 是一种开放标准(RFC 7519),用于在各方之间安全传输JSON对象。它以紧凑的URL安全字符串形式传递信息,适合用于无状态身份验证场景,例如前后端分离应用。
1. JWT结构拆解
一个JWT令牌由三部分组成,格式为:
Header.Payload.Signature
通过 .
连接(例:xxxxx.yyyyy.zzzzz
)
-
Header(头)
<JSON>
{"alg": "HS256", // 加密算法(如HS256, RS256)"typ": "JWT" // 类型固定为JWT }
-
Payload(载荷)
<JSON>
{"sub": "user123", // 用户标识(标准字段)"name": "John Doe", // 自定义声明"iat": 1664448000, // 签发时间"exp": 1665052800 // 过期时间 }
-
Signature(签名)
签名由Header、Payload和密钥生成,确保数据完整性:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret-key )
2. JWT核心优势
- 无状态性:服务端无需保存会话信息,天然适合分布式系统
- 安全性:数字签名防止篡改,结合HTTPS抵御中间人攻击
- 跨域友好:Token可轻松传递至不同域名的前端
- 扩展性:通过Payload自定义业务字段(如用户权限角色)
二、实战:Vue3 + Spring Boot集成JWT认证
1. 后端(Spring Boot)JWT配置
步骤1:添加Maven依赖
<XML>
<!-- JWT支持 -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>
步骤2:生成与验证JWT的工具类
<JAVA>
@Component
public class JwtUtils{@Value("${jwt.secret}")private String jwtSecret;//从yaml文件中jwt.secret中读取数据@Value("${jwt.expiration}")private int jwtExpirationMs;// 生成密钥对象(用于HS256)private SecretKey getSigningKey() {return Keys.hmacShaKeyFor(jwtSecret.getBytes());}// 生成令牌public String generateToken(User user) {return Jwts.builder().subject(user.getUsername())//设置负载部分.issuedAt(new Date()).expiration(new Date((new Date()).getTime() + jwtExpirationMs))//设置令牌有效期.signWith(getSigningKey())//设置密钥.compact();}// 解析令牌public Claims parseToken(String token) {return Jwts.parser().verifyWith(getSigningKey()).build().parseSignedClaims(token).getPayload();}// 验证令牌有效性public boolean validateToken(String token) {try {parseToken(token); // 自动校验签名和过期时间return true;} catch (JwtException | IllegalArgumentException e) {// 处理具体异常类型return false;}}// 从令牌获取用户名public String getUsernameFromToken(String token) {return parseToken(token).getSubject();}
}
在application.yaml文件中添加字段信息!!后面会有写道
步骤3:创建拦截器拦截请求解析token令牌
<JAVA>
package com.zht.interceptor;import com.zht.utils.JwtUtils;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;@Slf4j
@Component
public class CustomInterceptor implements HandlerInterceptor {@Autowiredprivate JwtUtils jwtUtils;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取请求头中的令牌String authHeader = request.getHeader("Authorization");String accept = request.getHeader("Accept");log.info("{}",accept);log.info("请求发送成功,正在验证令牌合理性");if (authHeader == null ) {throw new Exception("Authorization为空");}//根据前端设置的响应头为准如果前端响应头没有编写前缀不需要做校验注释掉以下代码即可if(!authHeader.startsWith("Bearer ")){throw new Exception("无效的Authorization头格式,需要Bearer token");}log.info("请求发送成功,正在验证令牌合理性");// 2.提取JWT令牌String token = authHeader.substring(7);try {// 3.解析验证令牌(示例密钥,实际应从配置读取)jwtUtils.parseToken(token);log.info("令牌验证成功");return true; // 验证通过} catch (ExpiredJwtException e) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("Token expired");} catch (JwtException e) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("Invalid token");}return false;}}
步骤4:创建实现WebMvcConfigurer接口的实现类,使上面自定义的拦截器生效,顺便解决跨域问题
<JAVA>
package com.zht.interceptor;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class CorsConfig implements WebMvcConfigurer {@Autowiredprivate CustomInterceptor customInterceptor;@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**") // 允许所有路径.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的请求方法.allowedHeaders("*") // 允许所有 Header.maxAge(3600); // 预检请求的缓存时间(单位:秒)}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(customInterceptor).addPathPatterns("/api/**") .excludePathPatterns("/api/login/**", "/api/register/**");//这里修改为自己的接口路径}
}
2. 前端(Vue3)Token管理
步骤1:登录接口获取Token,使用LocalStorage持久化存储到本地
<JAVASCRIPT>
// 登录请求示例
const login = () => {axios.post('/api/auth/login', { username, password }).then(res => {localStorage.setItem('token', res.data.token); // 存储Token至本地router.push('/dashboard'); // 跳转至你的项目首页面});
};
步骤2:请求拦截器自动附加Token
<JAVASCRIPT>
// Axios拦截器配置(src/utils/http.js)
import axios from 'axios';const http = axios.create({ baseURL: '/api' });http.interceptors.request.use(config => {const token = localStorage.getItem('token');if (token) {config.headers.Authorization = `Bearer ${token}`;}return config;
});http.interceptors.response.use(response => response,error => {if (error.response.status === 401) {// 处理Token过期:跳转登录页并清除本地TokenlocalStorage.removeItem('token');router.push('/login');}return Promise.reject(error);}
);export default http;
步骤3:页面路由守卫验证权限
<JAVASCRIPT>
// router.js配置
router.beforeEach((to, from, next) => {const isAuthenticated = localStorage.getItem('token') !== null;if (to.meta.requiresAuth && !isAuthenticated) {next('/login');} else {next();}
});
三、安全最佳实践
-
密钥管理
- 避免硬编码密钥,通过环境变量(
application.yml
)注入
<YAML>
jwt:secret: ${JWT_SECRET:default-secret}expiration-ms: 3600000
- 避免硬编码密钥,通过环境变量(
-
Token存储
- 推荐使用
HttpOnly Cookie
减少XSS攻击风险 - 若存localStorage,需代码加密(如
jsencrypt
库)
- 推荐使用
-
Token刷新机制
- 短期Token(5分钟)+ 长有效期Refresh Token,减少泄露风险
- 设计
/api/auth/refresh
接口刷新Token
四、对比传统会话认证
特性 | JWT | Session |
---|---|---|
存储位置 | 客户端 | 服务端 |
扩展性 | 适合分布式/微服务 | 需Session共享方案 |
安全性 | Signature防篡改 | Cookie属性控制(如Secure、SameSite) |
性能影响 | 每次请求携带完整Token | 服务端需查询Session |
总结:JWT为现代Web应用提供了简洁、安全的无状态认证方案。结合Spring Boot与Vue3的全栈实现,可快速搭建高可用身份验证流程。合理运用Token过期、刷新策略和加密存储,可大幅提升系统安全性。