Spring进行跨域配置,通过CrossOrigin注解,addCorsMappings方法以及添加CorsFilter过滤器的三种方式实现。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 注解方式进行跨域设置,可以加在方法或类上,如果加在controller上,那么就表明该controller下的所有接口都支持跨域
* CrossOrigin注解是在AbstractHandlerMethodMapping的内部类mappingRegistry的register方法中完成解析
* 根据注解中的属性来创建一个corsConfiguration对象,将CrossOrigin标注的请求方法的HandlerMethod与创建的corsConfiguration
* 一一对应,加入到corsLookup的Map映射中
* 当请求到达DispatcherServlet#doDispatch之后,调用AbstractHandlerMapping#getHandler获取执行链
* HandlerExecutionChain时,会从corsLookup中获取到对应的corsConfiguration对象
* 将corsConfiguration构建成一个corsInterceptor拦截器,然后由拦截器调用DefaultCorsProcessor#processRequest,来进行跨域请求的校验工作
*/
@CrossOrigin(
allowedHeaders = "*",
methods = {RequestMethod.POST},
originPatterns = "*",
maxAge = 3600,
allowCredentials = "true"
)
@PostMapping("/**")
public void cors() {
}
/**
* 通过FilterRegistrationBean来配置一个过滤器,处理跨域请求。
* 这个方式既可以处理跨域,也可以处理其他请求
* 本质是调用CorsFilter的doFilterInternal方法,触发对DefaultCorsProcessor#processRequest的调用
* 从而完成对跨域请求的处理。与@CrossOrigin与addCorsMappings方法的区别在于执行时机的不同
* CorsFilter的执行顺序在所有过滤器之前,而@CrossOrigin与addCorsMappings方法在所有过滤器之后。
*
* @return FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> corsFilter = new FilterRegistrationBean<>();
//创建CorsConfiguration对象 与addCorsMappings和@corsOrigin相同 都离不开CorsConfiguration
//区别在于以上两者是自动创建 而这里需要手动创建
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedMethods(List.of("*"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowedOriginPatterns(List.of("*"));
configuration.setMaxAge(3600L);
configuration.setAllowCredentials(true);
//创建UrlBasedCorsConfigurationSource对象 用于将CorsConfiguration对象与URL进行匹配
//并将这个映射匹配关系保存到UrlBasedCorsConfigurationSource的corsConfigurations集合中
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
//创建CorsFilter对象 并将UrlBasedCorsConfigurationSource对象作为参数传入
corsFilter.setFilter(new CorsFilter(source));
corsFilter.setOrder(-1); //设置过滤器优先级 值越小优先级越高
return corsFilter;
}
/**
* 配置全局跨域,通过重写WebMvcConfigurer的addCorsMappings方法实现
* 这种配置与@CrossOrigin注解相同,最终都是在corsInterceptor中发起对触发对DefaultCorsProcessor#processRequest的调用
* 执行要晚于CorsFilter
* registry的addMapping方法配置了一个CorsRegistration对象,该对象中包含了一个CorsConfiguration参数,用于保存addMapping的配置
* 在WebMvcConfigurationSupport#requestMappingHandlerMapping方法中触发addCorsMappings方法,将该方法创建的对象组装为一个
* UrlBasedCorsConfigurationSource对象,该对象中含有一个Map<String, CorsConfiguration> corsConfigurations,用于保存addMapping的配置
* 将这个UrlBasedCorsConfigurationSource对象赋值给AbstractHandlerMapping#corsConfigurationSource
* 当请求到达时,会在AbstractHandlerMapping#getHandler中进行处理,与@corsOrigin注解不同的是,这里获取CorsConfiguration是从
* corsConfigurationSource,而注解是从corsLookup集合中获取,
* 最后将获取到的CorsConfiguration对象组装为corsInterceptor拦截器后,调用方法进行跨域校验
*
* @param registry CorsRegistry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*")
.allowedHeaders("*")
.allowedOrigins("*")
.allowedOriginPatterns("*")
.maxAge(3600)
.allowCredentials(true);
}
}