一、构建基本拦截器
需要新建的类的位置结构:
1、新建自己的拦截类 UserTokenInterceptor 实现 HandlerInterceptor 拦截器接口,实现三个方法
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UserTokenInterceptor implements HandlerInterceptor {
/**
* 拦截请求,在controller调用之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("验证不通过,请求被拦截。。。");
/**
* false: 验证不通过,请求被拦截,不能继续访问
* true: 验证通过,请求放行。
*/
return false;
}
/**
* 请求访问controller之后,渲染视图之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 请求访问controller之后,渲染视图之后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2、新建自定义类实现 WebMvcConfigurer 接口
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//注册拦截器
@Bean
public UserTokenInterceptor userTokenInterceptor(){
return new UserTokenInterceptor();
}
/**
* 注册拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userTokenInterceptor())
.addPathPatterns("/hello");
//添加注册器
WebMvcConfigurer.super.addInterceptors(registry);
}
}
这里先测试拦截 /hello 地址。其controller如下,其实看不看都无所谓(你也可以设置成拦截自己的任意一个地址)
?3、启动springboot项目,访问刚刚被拦截的地址 localhost:8088/hello 。因为上面第1步preHandle
方法,设置了返回false,请求拦截。查看控制台,打印内容。基本拦截器完成。
(以下是举例使用,具体看个人情况)
二、拦截器具体逻辑实现(我这里实现会话判断)
1、在上述? 一、1. UserTokenInterceptor的覆盖方法 preHandle()?中,填写自己的拦截逻辑
import com.xpf.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UserTokenInterceptor implements HandlerInterceptor {
public static final String REDIS_USER_TOKEN = "redis_user_token";
@Autowired
private RedisOperator redisOperator;
/**
* 拦截请求,在controller调用之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// System.out.println("验证不通过,请求被拦截。。。");
//用户一登录,就会把用户token记录到redis和cookie中,所以前端可以拿到用户信息。
//且在需要判断用户权限的地方,在访问后端url时,都会把用户id和token存放到Header中,一起传入后端
String userId = request.getHeader("headerUserId");
String userToken = request.getHeader("headerUserToken");
if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)){
String uniqueToken = redisOperator.get(REDIS_USER_TOKEN+":"+userId);
if (StringUtils.isBlank(uniqueToken)){
System.out.println("redis没有信息,请登录。。。");
return false;
}else {
if (!uniqueToken.equals(userToken)){
System.out.println("账号在异地登录或不同设备登录,请重新登录");
return false;
}
}
}else {
System.out.println("请登录。。。");
return true;
}
/**
* false: 验证不通过,请求被拦截,不能继续访问
* true: 验证通过,请求放行。
*/
return true;
}
/**
* 请求访问controller之后,渲染视图之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 请求访问controller之后,渲染视图之后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
与前端约定的header,存储用户id和用户token。
用户一登录,就会把用户token记录到redis和cookie中,所以前端可以拿到用户信息。?且在需要判断用户权限的地方,在访问后端url时,都会把用户id和token存放到Header中,一起传入后端。
只有当用户登录,且前端cookie中的token和redis中token相同时,才能访问后端的接口,否则被拦截器拦截。
2、添加需要拦截的路径
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
//注册拦截器
@Bean
public UserTokenInterceptor userTokenInterceptor(){
return new UserTokenInterceptor();
}
/**
* 注册拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userTokenInterceptor())
.addPathPatterns("/hello")
.addPathPatterns("/shopcart/add")
.addPathPatterns("/shopcart/del")
.addPathPatterns("/address/list")
.addPathPatterns("/address/add")
.addPathPatterns("/address/update")
.addPathPatterns("/address/setDefault")
.addPathPatterns("/address/delete")
.addPathPatterns("/orders/*")
.addPathPatterns("/center/*")
.addPathPatterns("/userInfo/*")
.addPathPatterns("/myorders/*")
.addPathPatterns("/mycomments/*")
.excludePathPatterns("/myorders/deliver")
.excludePathPatterns("/orders/notifyMerchantOrderPaid")
;
//添加注册器
WebMvcConfigurer.super.addInterceptors(registry);
}
}
三、优化错误的返回
从上述二中实现拦截逻辑后,当被拦截时,应该将提醒登录的信息返回给前端,但是 boolean?preHandle() 方法返回值是boolean类型。和我们在整个项目约定的统一返回类型JSONResult不一致,因此需要使用 OutputStream 字符输出流,写到前端。
import com.xpf.utils.JSONResult;
import com.xpf.utils.JsonUtils;
import com.xpf.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public class UserTokenInterceptor implements HandlerInterceptor {
public static final String REDIS_USER_TOKEN = "redis_user_token";
@Autowired
private RedisOperator redisOperator;
/**
* 拦截请求,在controller调用之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// System.out.println("验证不通过,请求被拦截。。。");
//用户一登录,就会把用户token记录到redis和cookie中,所以前端可以拿到用户信息。
//且在需要判断用户权限的地方,在访问后端url时,都会把用户id和token存放到Header中,一起传入后端
String userId = request.getHeader("headerUserId");
String userToken = request.getHeader("headerUserToken");
if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)){
String uniqueToken = redisOperator.get(REDIS_USER_TOKEN+":"+userId);
if (StringUtils.isBlank(uniqueToken)){
// System.out.println("redis没有信息,请登录。。。");
returnErrorResponse(response, JSONResult.errorMsg("redis中没有信息,请登录..."));
return false;
}else {
if (!uniqueToken.equals(userToken)){
// System.out.println("账号在异地登录或不同设备登录,请重新登录");
returnErrorResponse(response, JSONResult.errorMsg("账号在异地登录或不同设备登录,请重新登录"));
return false;
}
}
}else {
// System.out.println("请登录。。。");
returnErrorResponse(response,JSONResult.errorMsg("请登录..."));
return false;
}
/**
* false: 验证不通过,请求被拦截,不能继续访问
* true: 验证通过,请求放行。
*/
return true;
}
/*
*/
public void returnErrorResponse(HttpServletResponse response, JSONResult result){
OutputStream out = null;
try {
response.setCharacterEncoding("utf-8");
response.setContentType("text/json");
out = response.getOutputStream();
out.write(JsonUtils.objectToJson(result).getBytes(StandardCharsets.UTF_8));
out.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (out != null)
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 请求访问controller之后,渲染视图之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 请求访问controller之后,渲染视图之后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
四、测试
1、访问 /hello
?2、访问需要登录的页面,前端拿到写入的信息,在做一个弹框提示
?
|