导入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
/*jwt工具类*/
public class JWTUtils {
//密钥
public static String secret ="xyg4444";
//过期时长
public static long expr = 3600*24*1000;
//1.生成token
public static String sign(User user){
//1.指定签名的时候使用的签名算法
SignatureAlgorithm hs256 = SignatureAlgorithm.HS256;
//2.生成签发时间
long nowMillis = System.currentTimeMillis();
Date date = new Date(nowMillis);
//3.创建playLoad的私有声明
Map<String,Object> claims = new HashMap<>();
claims.put("id",user.getId());
claims.put("userName",user.getUserName());
//4.生成签发人
String subject = user.getUserName();
JwtBuilder builder = Jwts.builder()
.setClaims(claims)
.setId(UUID.randomUUID().toString())
.setIssuedAt(date)
.setSubject(subject)
.signWith(hs256,secret);
//设置过期时间
Date exprDate = new Date(nowMillis + expr);
builder.setExpiration(exprDate);
return builder.compact();
}
//2'验证token
public static boolean verify(String token){
try {
if (StringUtil.isEmpty(token)) {
return false;
}
Jwts.parser().setSigningKey(secret).parseClaimsJwt(token).getBody();
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
//3.获取用户信息
public static User getUser(String token) {
try {
if (StringUtil.isEmpty(token)) {
throw new MyException("token不能为空");
}
if (verify(token)) {
Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJwt(token).getBody();
User user = new User();
user.setId(Integer.parseInt(claims.get("id") + ""));
user.setUserName(claims.get("userName") + "");
return user;
} else {
throw new MyException("超时或不合法token");
}
} catch (Exception e) {
throw new MyException("超时或不合法token");
}
}
//测试
public static void main(String[] args) {
User user = new User();
user.setId(2);
user.setUserName("admin");
System.out.println(sign(user));
}
}
实现HandlerInterceptor 接口
preHandle 业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理;
通过WebMvcConfigurer 进行配置使用
/*token拦截器*/
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
User user = JWTUtils.getUser(token);
if(user == null){
throw new MyException("超时或则不合法token");
}
//token续期
String newToken = JWTUtils.sign(user);
response.setHeader("token",newToken);
request.setAttribute("user",user);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
WebMvcConfigurer配置类其实是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制,可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter。基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口;
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
@Bean
public TokenInterceptor tokenInterceptor(){
return new TokenInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor()).addPathPatterns("/**").excludePathPatterns("/login");
}
}
ddInterceptors:拦截器
addInterceptor:需要一个实现HandlerInterceptor接口的拦截器实例
addPathPatterns:用于设置拦截器的过滤路径规则;addPathPatterns("/**")对所有请求都拦截
excludePathPatterns:用于设置不需要拦截的过滤规则
拦截器主要用途:进行用户登录状态的拦截,日志的拦截等。
解决 Signed Claims JWSs are not supported. 异常
原因
这个异常是由于使用了签名的 JWT(JWS),而 io.jsonwebtoken 库不支持这种方式导致的。当使用 parseClaimsJwt 方法解析带有签名的 JWT 时,它将忽略签名并且返回包含荷载(payload)的 Claims 对象。这会导致验证 JWT 的签名失败,并抛出异常。
解决方法
使用 parseClaimsJws 方法代替 parseClaimsJwt 方法,即可解决
Claims claims = Jwts.parser()
.setSigningKey(secret)
// 将 parseClaimsJwt 替换为 parseClaimsJws
.parseClaimsJws(token)
.getBody();
parseClaimsJws 与 parseClaimsJwt 的异同
相同
parseClaimsJwt 和 parseClaimsJws 两个方法的作用是一样的,都是解析 JWT 载荷并返回一个 Claims 对象。
不同
parseClaimsJwt 方法适用于未签名的 JWT。
parseClaimsJws 方法适用于带有 JWS 签名的 JWT。
应用场景:据所使用的 JWT 类型选择正确的方法来解析它
如果您使用的是带有 JWS 签名的 JWT,则必须使用 parseClaimsJws 方法才能正确解析 JWT 并验证签名。
如果使用的是未签名的 JWT,则可以使用 parseClaimsJwt 方法;