关于jwt的实现原理,可以自行查找资料。我们只关注如何实现。
一:首先引入依赖。目前有两个库实现了jwt的功能。
两个库都实现了jwt功能,只不过使用的api不太相同。使用其中任何一个都可以。通过查看maven官网,jjwt好久没有更新了。而java-jwt一直有更新。为了以后少点麻烦还是使用java-jwt.
二:创建jwt帮助类,实现三个功能,
1.创建token
最关键需要两个变量,一个是到期时间,一个是秘钥
private static final long EXPIRE_TIME = 5 * 60 * 1000;
private static final String TOKEN_SECRET = "ds_xjc_jwt_secret";
public static String getToken(User user) {
// Calendar instance = Calendar.getInstance();
// instance.add(Calendar.DATE, 7);/*默认令牌过期时间7天 财哥 2022/4/8 15:33*/
// instance.getTime();
Date expiresAt = new Date(System.currentTimeMillis() + EXPIRE_TIME);
JWTCreator.Builder builder = JWT.create();
builder.withClaim("xmId", user.getXmId()).withClaim("qsId",user.getQsId()).withClaim("userName", user.getUserName());
//builder.withAudience(user.getXmId()).withAudience(user.getQsId()).withAudience(user.getUserName());
return builder.withExpiresAt(expiresAt).sign(Algorithm.HMAC256(TOKEN_SECRET));/* 完成过期时间配置及加密 2022/4/8 15:34*/
}
2.验证token 如果不报异常就验证正确
public static boolean checkTokenVerify(String token) {
try {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (SignatureVerificationException e) {
log.error("无效签名! 错误 ->", e);
return false;
} catch (TokenExpiredException e) {
log.error("token过期! 错误 ->", e);
return false;
} catch (AlgorithmMismatchException e) {
log.error("token算法不一致! 错误 ->", e);
return false;
} catch (Exception e) {
log.error("token无效! 错误 ->", e);
return false;
}
}
3.获取token中数据,因为和验证调的是一样的api,只有先验证成功才能取数据,否则会出错
public static Map<String, String> parseToken(String token) {
HashMap<String, String> map = new HashMap<String, String>();
DecodedJWT decodedjwt = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build().verify(token);
// Claim xmId = decodedjwt.getClaim("xmId");
// Claim qsId = decodedjwt.getClaim("qsId");
// Claim userName = decodedjwt.getClaim("userName");
map.put("xmId", decodedjwt.getClaim("xmId").asString());
map.put("qsId", decodedjwt.getClaim("qsId").asString());
map.put("userName", decodedjwt.getClaim("userName").asString());
map.put("timeStamp",decodedjwt.getExpiresAt().toString());/* 过期时间 2022/4/8 21:49*/
return map;
}
三 集成进springboot,拦截请求
这里也有两种方式,一种是通过注解的方式加在指定的控制器上,另外一种是对特定的url进行拦截。
1. 注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JwtToken {
boolean required() default true;
}
2.写拦截器
@Autowired
JwtUtils jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
String token = request.getHeader("token"); // 从 http 请求头中取出 token
// 如果不是映射到方法直接通过 通过注解方式才需要以下代码
/*if (!(object instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//检查是否有JwtToken注释,有则跳过认证
if (method.isAnnotationPresent(JwtToken.class)) {
JwtToken passToken = method.getAnnotation(JwtToken.class);
if (passToken.required()) {
return true;
}
}*/
//为空就返回错误
if (null == token || "".equals(token.trim())) {
return false;
}
if(jwtUtil.checkTokenVerify(token)){
// Map<String, String> map = jwtUtil.parseToken(token);
// String userId = map.get("userId");
// String userRole = map.get("userRole");
// long timeOfUse = System.currentTimeMillis() - Long.parseLong(map.get("timeStamp"));
// //1.判断 token 是否过期
// if (timeOfUse < refreshTime) {
// log.info("token验证成功");
// return true;
// }
// //超过token刷新时间,刷新 token
// else if (timeOfUse >= refreshTime && timeOfUse < expiresTime) {
// httpServletResponse.setHeader("token",tokenUtil.getToken(userId,userRole));
// log.info("token刷新成功");
// return true;
// }
// //token过期就返回 token 无效.
// else {
// throw new TokenAuthExpiredException();
// }
return true;
}else{
return false;
}
}
3.集成
@Configuration
public class JwtWebMvcConfigurer implements WebMvcConfigurer {
@Autowired
JwtHandlerInterceptor jwtHandlerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtHandlerInterceptor).addPathPatterns("/mobile/**").excludePathPatterns("/mobile/login");//针对指定路径进行拦截,排除登录页面
}
}
四 使用
1. 如果通过注解方式的,就在方法上加注解
2.如果是通过url的就不用加注解