Spring Boot拦截器Intercepter详解
Intercepter是由Spring提供的Intercepter拦截器,主要应用在日志记录、权限校验等安全管理方便。
使用过程
1.创建自定义拦截器,实现HandlerInterceptor接口,并按照要求重写指定方法
HandlerInterceptor接口源码:
public interface HandlerInterceptor {default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true;}default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {}default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {}
}
根据源码可看出HandlerInterceptor接口提供了三个default方法,这三个方法作用不同,用户想要自定义个一个指定拦截规则的拦截器,需要重写其中一个或者多个方法,这三个方法作用如下:
- perHandle:preHandle方法的作用是,当请求在进入controller之前拦截请求,对请求进行预处理,比如登录验证(cookie,token,referer)或者单点登录cookie解析都可以在这方法中进行。该方法的返回值,如果返回true,表示放行至controller业务层,如果false,表示请求非法,结束请求并返回错误信息。
- postHandler:postHandle方法是在请求被controller处理完但是还未传递到业务模板进行渲染拦截,即controller处理完,返回ModelAndView 之前执行该方法,可以操控ModelAndView的值;所以该方法多了一个参数,ModelAndView,这个参数包含了controller处理完后需要传递的Model参数,因此,我们可以在该方法通过ModelAndView对象对返给前端的额视图做一定的修改。
- afterCompletion:afterCompletion方法作用就是做些收尾工作,在ModelAndView返回前端进行渲染后执行,比如有时候我们需要把每个线程的局部变量(如User信息)放入到TheradLocal中,为了防止内存泄露,在最后需要清除ThreadLocal的内容,此操作就可以放在该方法中执行。
自定义一个获取并返回某个静态资源的内容已整个请求所花费时间的时间拦截器
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class MyInterceptor implements HandlerInterceptor {private static final Logger LOGGER = LoggerFactory.getLogger(MyInterceptor.class);private static final ThreadLocal<Long> START_THREAD_LOCAL = new ThreadLocal<>();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {String uri = request.getRequestURI();LOGGER.info(uri + " preHandle");Long startTime = System.currentTimeMillis(); //获取开始时间START_THREAD_LOCAL.set(startTime); //线程绑定变量(该数据只有当前请求的线程可见)return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {String uri = request.getRequestURI();LOGGER.info(uri + " postHandle");Long startTime = START_THREAD_LOCAL.get();//得到线程绑定的局部变量(开始时间)Long endTime = System.currentTimeMillis(); //2、结束时间Long time = endTime - startTime;LOGGER.info("http request all time: " + time + "ms");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {String uri = request.getRequestURI();LOGGER.info(uri + " afterCompletion");if (START_THREAD_LOCAL != null) {START_THREAD_LOCAL.remove(); // 移除ThreadLocal中的局部变量}}
}
2.添加配置类,实现WebMvcController接口,并添加@Configuration注解,在配置类中,重写addIntercepters方法,添加要拦截的url以及url白名单(需要排除拦截的url)
WebMvcConfigurer源码:
public interface WebMvcConfigurer {default void configurePathMatch(PathMatchConfigurer configurer) {}default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}default void configureAsyncSupport(AsyncSupportConfigurer configurer) {}default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}default void addFormatters(FormatterRegistry registry) {}default void addInterceptors(InterceptorRegistry registry) {}default void addResourceHandlers(ResourceHandlerRegistry registry) {}default void addCorsMappings(CorsRegistry registry) {}default void addViewControllers(ViewControllerRegistry registry) {}default void configureViewResolvers(ViewResolverRegistry registry) {}default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}@Nullabledefault Validator getValidator() {return null;}@Nullabledefault MessageCodesResolver getMessageCodesResolver() {return null;}
}
根据源码可以看出,WebMvcConfigurer提供了多个方法,并且也都是default方法,也是根据我们自定义配置,重写其中一个或者多个方法,这里就介绍两个常用的方法:
- addInterceptors:从该方法名就可以了解到该方法是添加拦截器,即将拦截器交给IOC去执行,拦截器需要拦截的路径以及需要排除拦截的路径在该方法中配置。
- addResourceHandlers:该方法的作用是配置静态资源路径。即某些请求需要读取某个路径下的静态资源内容,需要配置该静态资源的路径,通过该方法可以统一给这些请求配置指定静态资源路径 。
实例:
import com.eureka.intercrpotor.MyInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Beanpublic MyInterceptor myInterceptor() {return new MyInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(myInterceptor()) // 添加拦截器.addPathPatterns("/**") // 配置拦截请求url( ** 表示拦截所有请求url).excludePathPatterns("/hello"); // 排除某些不需要拦截的请求url(即带有/hello请求不会被拦截)}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/**") // 配置需要添加静态资源的请求url.addResourceLocations("classpath:/mydata/"); //配置静态资源路径}
}
测试:
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TestController {@GetMapping("/test/interceptor")public ResponseEntity<String> testInterceptor() {return ResponseEntity.ok("successful");}
}
静态资源:
启动项目后访问 localhost:60011/test/interceptor:
控制台打印的日志:
我们再通过url访问静态资源请求 localhost:60011/test.jpg