Spring Boot中使用拦截器
13.1)应用场景:
拦截器是 AOP 的一种实现,专门拦截对动态资源的后台请求,即拦截对控制层的请 求。使用场景比较多的是判断用户是否有权限请求后台,更拔高一层的使用场景也有,比如拦截器可以 结合 websocket 一起使用,用来拦截 websocket 请求,然后做相应的处理等等。拦截器不会拦截静态 资源,Spring Boot 的默认静态目录为 resources/static,该目录下的静态页面、js、css、图片等等, 不会被拦截.
13.2定义拦截器
定义拦截器,只需要实现 HandlerInterceptor 接口, 该接口中有三个方法:
preHandle(……) 、 postHandle(……) 和 afterCompletion(……) 。
preHandle(……) 方法:该方法的执行时机是,当某个 url 已经匹配到对应的 Controller 中的某
个方法,且在这个方法执行之前。所以 preHandle(……) 方法可以决定是否将请求放行,这是通
过返回值来决定的,返回 true 则放行,返回 false 则不会向后执行。
postHandle(……) 方法:该方法的执行时机是,当某个 url 已经匹配到对应的 Controller 中的某
个方法,且在执行完了该方法,但是在 DispatcherServlet 视图渲染之前。所以在这个方法中有
个 ModelAndView 参数,可以在此做一些修改动作。
afterCompletion(……) 方法:顾名思义,该方法是在整个请求处理完成后(包括视图渲染)执
行,这时做一些资源的清理工作,这个方法只有在 preHandle(……) 被成功执行后并且返回 true
才会被执行。
13.2.1)自定义拦截类实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
private Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
String methodName = method.getName();
logger.info("方法{}被拦截", methodName);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("方法被执行,但是视图还未渲染");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("方法执行完毕,进行资源清理");
}
}
13.2.2)实现WebMvcConfigurer接口进行拦截配置
实现WebMvcConfigurer的这种配置会自动过滤静态资源;
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
省略controller类
测试结果
10:52:16.316 [http-nio-8080-exec-1] INFO c.e.h.interceptors.MyInterceptor - 方法test被拦截
10:52:16.344 [http-nio-8080-exec-1] INFO c.e.h.interceptors.MyInterceptor - 方法被执行,但是视图还未渲染
10:52:16.344 [http-nio-8080-exec-1] INFO c.e.h.interceptors.MyInterceptor - 方法执行完毕,进行资源清理
定义哪些不用拦截
取消拦截操作
如果我要拦截所有 /admin 开头的 url 请求的话,需要在拦截器配置中添加这个前缀,但是 在实际项目中,可能会有这种场景出现:某个请求也是 /admin 开头的,但是不能拦截,比如 /admin/login 等等
解决方案:
1.使用excludePathPatterns("/adminUser/login")
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/adminUser/login");
}
2.可以定义一个注解
该注解专门用来取消拦截操作,如果某个 Controller 中的方法我们 不需要拦截掉,即可在该方法上加上我们自定义的注解即可,下面先定义一个注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UnInterception {
}
测试
controller类
@RestController
@RequestMapping("/intercept")
public class IntercepController {
@UnInterception
@RequestMapping("/hello")
public String test() {
return "success";
}
interceptor拦截类
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
String methodName = method.getName();
if (method.getAnnotation(UnInterception.class)!=null) {
logger.info("方法{}不被拦截", methodName);
return true;
}
logger.info("方法{}被拦截", methodName);
return false;
}
访问 http://localhost:8080/intercept/hello
结果
11:34:24.648 [http-nio-8080-exec-1] INFO c.e.h.interceptors.MyInterceptor - 方法test不被拦截
11:34:24.676 [http-nio-8080-exec-1] INFO c.e.h.interceptors.MyInterceptor - 方法被执行,但是视图还未渲染
11:34:24.679 [http-nio-8080-exec-1] INFO c.e.h.interceptors.MyInterceptor - 方法执行完毕,进行资源清理