1.创建 WebInterceptorConfig
@Configuration
public class WebInterceptorConfig implements WebMvcConfigurer {
/*
* 自定义拦截器
*/
@Bean
public WebConstantsInterceptor webConstantsInterceptor() {
return new WebConstantsInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 常量拦截器
registry.addInterceptor(webConstantsInterceptor())
//需要拦截的地址,全局拦截
.addPathPatterns("/**")
//过滤地址
.excludePathPatterns("/auth/**","/profile/**");
}
}
2.创建自定义的拦截器 WebConstantsInterceptor 其实就是继承 HandlerInterceptor 进行重写
@Component
public class WebConstantsInterceptor implements HandlerInterceptor {
@Autowired
private RedisUtils redisUtils;
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {//在访问之后执行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 方法名 preHandle
* 描述 在访问项目之前验证浏览器token session是否存在
* 作者 huangshun
* 时间 2020/10/27 0027 上午 9:24
*
* @Param [request, response, handler]
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//TODO 跨域配置
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
response.setHeader("Access-Control-Allow-Headers", ((HttpServletRequest) request).getHeader("Access-Control-Request-Headers"));
//TODO 跨域时首先会发送一个option请求,这里我们给option请求直接返回正常状态
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
response.setStatus(HttpStatus.OK.value());
return false;
}
//TODO response 返回 json
response.setHeader("x-frame-options", "SAMEORIGIN");
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
//TODO 携带 token 的方式一般为 Bearer 认证方式,自定义Header头部,参数形式,还有cookie (这里没有加cookie 方式)
//TODO 1.获取Bearer token
String authorization = request.getHeader("Authorization");
Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-:._~+/]+=*)$", Pattern.CASE_INSENSITIVE);//<token>的值就是真实的表达式配置的值
String token = null;
boolean flag = true;
//TODO 1.1如果有 Bearer token
if (StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
Matcher matcher = authorizationPattern.matcher(authorization);
if (!matcher.matches()) {
flag = true;
}else {
token = matcher.group("token");
flag = false;
}
}
//TODO 1.2则获取其他头部 token,此处token为自己定义的token 名称,这里用token 为名称
if(flag) {
token = request.getHeader("token");
if (token == null) {
flag = true;
}else {
flag = false;
}
}
//TODO 1.3则获取请求参数中是否,此处token为自己定义的token 名称,这里用token 为名称
if(flag) {
token = request.getParameter("token");
if (token == null) {
// (ResultMsg为自定义的封装结果)
response.getWriter().write(JSONUtil.parseObj(ResultMsg.error("token过期")).toString());
return false;
}
}
//TODO 1.4 验证token 是否过期,因为登录使用了redis 缓存,所以这里根据token从缓存中获取登录的用户信息 (ResultMsg为自定义的封装结果)
Object userInfo = redisUtils.get(token);
if (userInfo == null) {
// (ResultMsg为自定义的封装结果)
response.getWriter().write(JSONUtil.parseObj(ResultMsg.error("token过期")).toString());
return false;
}
// TODO 2.token存在,验证session,这里主要是判断session会话是否已存在当前模块中,单点登录模式下比较好理解(这一步看个人习惯是否使用,因为前面已经使用了Redis缓存了)
HttpSession session = request.getSession();
// TODO 这里主要是从session会话获取存在会话,(admin 是自己定义获取会话的key )
Object attribute = session.getAttribute("admin");
Users user = null;
//TODO 2.1未登录状态
if (attribute != null) {
JSONObject jsonObject = JSONUtil.parseObj(attribute);
user = JSONUtil.toBean(jsonObject, Users.class);
}
//TODO 2.2 session不存在则属于未登录状态
if (user == null) {
//存储session
session.setAttribute("admin", userInfo);
//赋值给user
user = JSONUtil.toBean(JSONUtil.parseObj(userInfo), Users.class);
}
//TODO 2.3 二次确认是否有用户信息
if (user == null) {
// (ResultMsg为自定义的封装结果)
response.getWriter().write(JSONUtil.parseObj(ResultMsg.error("token过期")).toString());
return false;
}
return true;
}
}