在springboot项目中加入如下依赖
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.19.1</version> </dependency>
编写Jwt工具类JwtUtil
public class JwtUtil { /** * token 过期时间, 单位: 秒. 这个值表示 30 天 */ private static final long TOKEN_EXPIRED_TIME = 30 * 24 * 60 * 60; public static final String jwtId = "tokenId"; /** * jwt 加密解密密钥(可自行填写) */ private static final String JWT_SECRET = "1234567890"; /** * 用户登录成功后生成jwt * 使用Hs256算法 * 三部分组成 头部+荷载+签证信息 * @param ttlMillis jwt过期时间 * @return */ public static String createJwt(long ttlMillis, Integer id,String username){ // header部分,jwt已经封装好了 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // jwt生成时间 当前时间 long nowMillis = System.currentTimeMillis(); Date date = new Date(nowMillis); // payload 荷载部分(存放有效信息的地方,包含标准中注册的声明、公共声明、私有声明) // 创建私有声明 Map<String,Object> claims = new HashMap<>(); claims.put("id", id); claims.put("username",username); // 为payload添加标准声明和私有声明(new一个JwtBuilder,设置jwt的body) JwtBuilder jwtBuilder = Jwts.builder() // 先设置自己创建的私有声明,要是写在标准声明后面,会覆盖掉标准声明 .setClaims(claims) // 设置jti(jwt id),主要用来作为一次性token,从而回避重放攻击 .setId(UUID.randomUUID().toString()) // 设置iat jwt签发时间 .setIssuedAt(date) // 设置jwt的所有人 .setSubject(username) // 设置签名使用的签名算法和签名使用的秘钥 .signWith(signatureAlgorithm, JWT_SECRET); // 设置jwt的过期时间 if(ttlMillis>= 0){ long expMillis = ttlMillis+nowMillis; Date expDate = new Date(expMillis); jwtBuilder.setExpiration(expDate); } System.out.println("生成jwt"); return jwtBuilder.compact(); } /** * 验证jwt */ public static Claims verifyJwt(String token) { // 签名秘钥(与生成签名的秘钥一样) // 得到DefaultJwtParser return Jwts.parser() // 设置签名的秘钥 .setSigningKey(JWT_SECRET) // 设置需要解析的jwt .parseClaimsJws(token).getBody(); } }
编写后端的Jwt拦截器JwtInterceptor
@Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception { String token = request.getHeader("token"); String msg = "缺少token"; if(token ==null){ response.setContentType("application/json; charset=UTF-8"); ObjectMapper mapper = new ObjectMapper(); String result = mapper.writeValueAsString(R.error(401,msg)); response.getWriter().print(result); return false; } try { Claims claims = JwtUtil.verifyJwt(token); if (claims!=null){ return true; } }catch (SignatureVerificationException e){ msg= "无效签名"; }catch (TokenExpiredException e){ msg= "已过期"; }catch (AlgorithmMismatchException e){ msg ="算法不一致"; }catch (Exception e){ msg="无效身份信息"; } response.setContentType("application/json; charset=UTF-8"); ObjectMapper mapper = new ObjectMapper(); String result = mapper.writeValueAsString(R.error(401,msg)); response.getWriter().print(result); return false; } }
编写Jwt拦截器的配置
@Configuration public class JwtInterceptorConfig implements WebMvcConfigurer { @Autowired private JwtInterceptor jwtInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // 用户拦截器 registry.addInterceptor(jwtInterceptor) // 需要拦截的请求 .addPathPatterns("/**") //需要放行的请求 .excludePathPatterns("/captcha","/login"); // 添加swagger-ui的放行路径 // .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**","/doc.html/**"); } }
前端编写
首先是登陆成功将token放到本地保存起来
window.localStorage.setItem("token", resp.token)
然后是每次发请求带上token
// 请求发生前处理 config.headers = { // 每次请求前带上Token token: window.localStorage.getItem("token") }