一: 导入依赖
<!-- jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
二: 设置jwt配置类
package com.lemon.move.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import org.springframework.stereotype.Component;
/**
* token 工具类
*/
@Component
public class JwtUtils {
// 过期时间 一小时 毫秒
private static long expire = 3600000;
// 秘钥
private static String secret = "HSyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9";
/**
* 创建一个token
*
* @param userId
* @return
*/
public String generateToken(String userId) {
Date now = new Date();
Date expireDate = new Date(now.getTime() + expire);
return Jwts.builder().setHeaderParam("type", "JWT").setSubject(userId).setIssuedAt(now)
.setExpiration(expireDate).signWith(
SignatureAlgorithm.HS512, secret).compact();
}
/**
* 解析token
*/
public Claims getClaimsByToken(String token) {
try {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
System.out.println("validate is token error");
return null;
}
}
/**
* 判断 token 是否过期
*/
public boolean isTokenExpired(Date expiration){
return expiration.before(new Date());
}
}
三: 配置jwt的拦截器
package com.lemon.move.interceptor;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.lemon.move.common.exception.BusinessException;
import com.lemon.move.common.exception.UnifiedExceptionHandler;
import com.lemon.move.common.result.R;
import com.lemon.move.common.result.ResponseEnum;
import com.lemon.move.config.CommonConfig;
import com.lemon.move.utils.JwtUtils;
import com.lemon.move.utils.RedisUtil;
import io.jsonwebtoken.Claims;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.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.handler.HandlerInterceptorAdapter;
/**
* 创建一个 token 拦截器.
* 需要继承 HandlerInterceptorAdapter,并且声明为spring的组件
* 异常类也是我自己定义的,可以根据自己的实际情况进行修改
*/
@Slf4j
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private RedisUtil redisUtil;
// 重写 前置拦截方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 从请求头中获取token
String token = request.getHeader("token");
log.info("开始拦截token:" + token);
// 判断 token 是否存在
if (token == null || "".equals(token)) {
log.info("未登录的状态,直接跳转到登录前台页面");
/*未登录的话需要跳转到管理端登录页面*/
response.sendRedirect("http://www.baidu.com");
// 这里可以自定义 抛出 token 异常
throw new BusinessException(ResponseEnum.NOT_LOGGED_IN);
}
// 解析token
Claims claim = jwtUtils.getClaimsByToken(token);
if (StringUtils.checkValNull(claim)) {
log.info("token 解析错误,直接跳转到登录前台页面");
/*未登录的话需要跳转到管理端登录页面*/
response.sendRedirect("http://www.baidu.com");
// 这里可以自定义 抛出 token 异常
throw new BusinessException(ResponseEnum.PARAM_ERROR);
}
// 判断 token 是否过期
Date expiration = claim.getExpiration();
boolean tokenExpired = jwtUtils.isTokenExpired(expiration);
if (tokenExpired) {
log.info("token已过期,直接跳转到登录前台页面");
/*未登录的话需要跳转到管理端登录页面*/
response.sendRedirect("http://www.baidu.com");
// 这里可以自定义 抛出 token 异常
throw new BusinessException(ResponseEnum.TOKEN_STALE_DATED);
}
// 从 token 中获取员工信息 ,严谨点的话应该去数据库里面查,但是有损性能就不做了
String userId = claim.getSubject();
if (StringUtils.isBlank(userId)) {
log.info("校验这个token的真实性");
/*未登录的话需要跳转到管理端登录页面*/
response.sendRedirect("http://www.baidu.com");
// 这里可以自定义 抛出 token 异常
throw new BusinessException(ResponseEnum.PARAM_ERROR);
}
/**
* 前提是,登录成功后生成token的时候,需要存放在redis 中一份
* 根据userId和业务前缀组成的key,去redis中查询是否有对应的值
* 1.存在,证明可以登录
* 2.不存在,证明用户点了注销登录或者修改了密码,但是这个token还没有过期,请求中还在使用之前的token,这个时候应该让用户重新登录,并且在redis中将这个token删除
*/
// 根据userId去redis中校验一下token是否已经修改了
// commonConfig.getLogin_token() 是我自己写的redis前缀,您可以根据自己的实际情况进行修改
String redisToken = (String) redisUtil.get(commonConfig.getLogin_token() + userId);
if (StringUtils.isBlank(redisToken) || !token.equals(redisToken)) {
log.info("token不合格");
/*未登录的话需要跳转到管理端登录页面*/
response.sendRedirect(commonConfig.getWeb_login_url());
// 这里可以自定义 抛出 token 异常
throw new BusinessException(ResponseEnum.PARAM_ERROR);
}
return true;
}
}
四: 配置拦截器,去做统一管理
package com.lemon.move.interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 设置拦截器.
* 打上configuration 注解,标注为配置项
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
// 注入 token 拦截器
@Autowired
private TokenInterceptor interceptor;
/**
* 重写添加拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加自定义拦截器,并拦截对应 url
registry.addInterceptor(interceptor).addPathPatterns("项目路径")
.excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**") //放行swagger路径
.excludePathPatterns("/move/api/login/**"); // 放行其他路径
}
}