背景: 今天有一个需求需要拦截除登录相关请求以外的所有请求,并查看request 中是否包含指定的信息,而自然就想到了使用拦截器就可以轻松实现
编写拦截器,获取请求头信息中的test,并打印出来
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String testData = request.getHeader("test");
System.err.println(testData);
return true;
}
}
添加拦截器
@Configuration
public class AuthorizationConfig implements WebMvcConfigurer {
@Autowired
AuthorizationInterceptor authorizationInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor).addPathPatterns("/**");
}
}
而当我打上断点后,这里却出现了2个问题
- 请求却没有到这里来
- 前端反馈说Swagger文档地址也打不开了
到这里说明我们的配置并没有生效,或者说都失效了,那么有可能是什么原因导致我们的拦截器配置失效呢?
突然想起为支持跨域而添加的配置,如下:
@Configuration
public class CorsConfig extends WebMvcConfigurationSupport {
/* @Autowired
private AuthorizationInterceptor authorizationInterceptor;*/
@Override
protected void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("*").maxAge(3600);
}
}
这里我们也没发现什么异常的地方,只能往SpringBoot 自动加载WebMvc配置的地方看了,默认情况下,SpringBoot是启用WebMvcAutoConfiguration,我们打开WebMvcAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
//@ConditionalOnClass注解表示在环境中存在指定的类才加载此配置
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
//@ConditionalOnMissing注解表示在环境中出现了指定的类则不加载此配置
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
public static final String DEFAULT_PREFIX = "";
public static final String DEFAULT_SUFFIX = "";
private static final String[] SERVLET_LOCATIONS = { "/" };
....
}
结论: 也就是说我们为添加跨域支持时继承了WebMvcConfigurationSupport,(可以使用webmvcconfigureAdapter ,但是因为我使用的是SpringBoot2.x 以上的版本,该抽象类已经被废弃)而在WebMvc 自动装配的配置类中,指定了WebMvc的加载条件
- 当环境中(IOC容器)存在Servlet、 DispatcherServlet、 WebMvcConfigurer 时加载WebMvc配置
- 当环境中存在WebMvcConfigurationSupport Bean时不加载此配置,也就是说不能有WebMvcConfigurationSupport
所以这也就解释了我们新添加的拦截器不生效的原因了!
到这里我们还有如下2个问题:
- 如何添加自定义的拦截器
- Swagger文档访问不了的情况
这2个问题本身就是我们的WebMvc配置因为我们继承使用WebMvcConfigurationSupport 未被加载的原因,查看WebMvcConfigurationSupport 源码发现,在Spring 要获取拦截器链时会调用一个交给子类实现的钩子方法,如下:
//SpringBoot 启动时会调用该方法获取拦截器
protected final Object[] getInterceptors(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
//这里就是交给子类实现的钩子方法,空实现
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
//交给子类实现的钩子方法
protected void addInterceptors(InterceptorRegistry registry) {
}
所以我们可以直接在添加跨域配置的配置类中重写addInterceptors方法,并在里面添加swagger的HTML的映射
@Configuration
public class CorsConfig extends WebMvcConfigurationSupport {
@Autowired
private AuthorizationInterceptor authorizationInterceptor;
@Override
protected void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("*").maxAge(3600);
}
//这里添加自定义的拦截器
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor).addPathPatterns("/**");
super.addInterceptors(registry);
}
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations(
"classpath:/static/");
registry.addResourceHandler("swagger-ui.html").addResourceLocations(
"classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}
}
到这里请求能够到达我们自定义的拦截器中,swagger接口文档地址也能够正常访问!
总结:
- @ConditionalOnClass注解表示在环境中存在指定的类才加载此配置
- @ConditionalOnMissing注解表示在环境中出现了指定的类则不加载此配置