要了解Spring拦截器的作用,我们需要先解释一下HTTP请求的执行链。DispatcherServlet捕获每个请求。调度员做的第一件事就是将接收到的URL和相应的controller进行映射(controller必须恰到好处地处理当前的请求)。但是,在到达对应的controller之前,请求可以被拦截器处理。这些拦截器就像过滤器。只有当URL找到对应于它们的映射时才调用它们。在通过拦截器(拦截器预处理,其实也可以说前置处理)进行前置处理后,请求最终到达controller。之后,发送请求生成视图。但是在这之前,拦截器还是有可能来再次处理它(拦截器后置处理)。只有在最后一次操作之后,视图解析器才能捕获数据并输出视图。
SpringBoot要实现拦截器分为两步:
1、定义拦截器。实现HandlerInterceptor接口,重写里面需要的三个比较常用的方法,实现自己的业务逻辑代码
2、另起一个类继承WebMvcConfigurer,注入上面定义的拦截器,添加@Configuration注解,重写addInterceptors方法,编写对哪些请求拦截和不拦截。
定义拦截器
@Component
public class UserCheckInterceptor implements HandlerInterceptor{
/**
* 预处理回调方法,实现处理器的预处理,第三个参数为响应的处理器,自定义Controller
* 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
return false;
}
/**
* 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
实现HandlerInterceptor接口的话需要重写这三个方法,不能少。可以替换成实现HandlerInterceptorAdapter适配器接口,只需要实现自己想实现的方法即可。
装载拦截器
自定义一个继承自WebMvcConfigurerAdapter的拦截器配置类,并实现其中的addInterceptors方法,将我们的拦截器添加进拦截器
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Resource
LoginInterceptor loginInterceptor;
/**
* 拦截
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
// 拦截的请求
.addPathPatterns("/**")
// 不拦截的请求(放行)
.excludePathPatterns(
"/test/**",
"/redis/**",
"/user/login",
"/category/all",
"/ebook/all",
"/ebook/search",
"/doc/all/**",
"/doc/vote/**",
"/doc/getContent/**"
);
}
}
使用示例
登录拦截
@Component
public class LoginInterceptor implements HandlerInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(LoginInterceptor.class);
@Resource
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 打印请求信息
LOG.info("------------- LoginInterceptor 开始 -------------");
long startTime = System.currentTimeMillis();
request.setAttribute("requestStartTime", startTime);
// OPTIONS请求不做校验,
// 前后端分离的架构, 前端会发一个OPTIONS请求先做预检, 对预检请求不做校验
if(request.getMethod().toUpperCase().equals("OPTIONS")){
return true;
}
String path = request.getRequestURL().toString();
LOG.info("接口登录拦截:,path:{}", path);
//获取header的token参数
String token = request.getHeader("token");
LOG.info("登录校验开始,token:{}", token);
if (token == null || token.isEmpty()) {
LOG.info( "token为空,请求被拦截" );
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
Object object = redisTemplate.opsForValue().get(token);
if (object == null) {
LOG.warn( "token无效,请求被拦截" );
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
} else {
LOG.info("已登录:{}", object);
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
long startTime = (Long) request.getAttribute("requestStartTime");
LOG.info("------------- LoginInterceptor 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// LOG.info("LogInterceptor 结束");
}
}
80%的人都看过的文章
本文来自凡蜕博客(https://blog.ysboke.cn), 转载请带上地址.。