写点什么

跨域问题之 Spring 的跨域的方案

作者:周杰伦本人
  • 2022 年 7 月 09 日
  • 本文字数:2732 字

    阅读完需:约 9 分钟

跨域问题之 Spring 的跨域的方案

@CrossOrigin

@CrossOrigin 可以添加到方法上,也可以添加到 Controller 上


AbstractHandlerMethodMapping 的内部类 MappingRegistry 的 register:


public void register(T mapping, Object handler, Method method) {   // Assert that the handler method is not a suspending one.   if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {      Class<?>[] parameterTypes = method.getParameterTypes();      if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {         throw new IllegalStateException("Unsupported suspending handler method detected: " + method);      }   }   this.readWriteLock.writeLock().lock();   try {      HandlerMethod handlerMethod = createHandlerMethod(handler, method);      validateMethodMapping(handlerMethod, mapping);      this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); }
String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); }
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); }
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); }}
复制代码


  1. @CrossOrigin 注解在 AbstractHandlerMethodMapping 的内部类 MappingRegistry 的 register 方法中完成解析,@CrossOrigin 注解中的内容会被解析成一个配置对象 CorsConfiguration

  2. 将 @CrossOrigin 所标记的请求方法对象 HandlerMethod 和 CorsConfiguration 一一对应存入 corsLookup 的 map 集合中

  3. 当请求到达 DispatcherServlet 的 doDispatch 方法之后,调用 AbstractHandlerMapping 的 getHandler 方法获取执行链 HandlerExecutionChain 时,会从 map 中获取 CorsConfiguration 对象

  4. 根据获取到的 CorsConfiguration 对象构建一个 CorsInterceptor 拦截器

  5. 在 CorsInterceptor 拦截器中触发对 CorsProcessor 的 processRequest 方法调用,跨域请求的校验工作将在该方法中完成。

addCorsMappings

@CrossOrigin 是添加在不同的 Controller 中 全局配置


@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {    @Override    public void addCorsMappings(CorsRegistry registry) {        registry.addMapping("/**")                .allowedMethods("*")                .allowedOrigins("*")                .allowedHeaders("*")                .allowCredentials(false)                .exposedHeaders("")                .maxAge(3600);    }}
复制代码


全局配置和 @CrossOrigin 注解相同,都是在 CorsInterceptor 拦截器中触发对 CorsProcessor 的 processRequest 方法调用,最终在该方法中完成跨域请求的校验工作


  1. registry.addMapping("/**")方法中配置了一个 CorsRegistration 对象,该对象中包含了一个路径拦截规则,同时 CorsRegistration 还包含了一个 CorsConfiguration 配置对象,该对象用来保存这里跨域相关的配置。

  2. 在 WebMvcConfigurationSupport 的 requestMappingHandlerMapping 方法中触发了 addCorsMappings 方法执行,将获取到的 CorsRegistration 对象重新组装成一个 UrlBasedCorsConfigurationSource 对象,该对象保存了拦截规则和 CorsConfiguration 对象的映射关系。

  3. 将新建的 UrlBasedCorsConfigurationSource 对象赋值给 AbstractHandlerMapping 的 corsConfigurationSource 属性

  4. 当请求到达时的处理方法和 @CrossOrigin 注解处理流程一样,在 AbstractHandlerMapping 的 getHandler 方法处理,从 corsConfigurationSource 中获取 CorsConfiguration 配置对象,而 @CrossOrigin 从 map 中获取 CorsConfiguration 对象。如果两处都可以获取到 CorsConfiguration 对象,则获取到的对象属性值进行合并。

  5. 根据获取到的 CorsConfiguration 对象构造 CorsInterceptor 拦截器

  6. 在 CorsInterceptor 拦截器中触发对 CorsProcessor 的 processRequest 方法调用,跨域请求的校验工作将在该方法中完成。


这里的跨域校验是通过 DispatcherServlet 中的方法触发的,DispatcherServlet 在 Filter 之后执行

CorsFIlter

@Configurationpublic class WebMvcConfig {    @Bean    FilterRegistrationBean<CorsFilter> corsFilter() {        FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();        CorsConfiguration corsConfiguration = new CorsConfiguration();        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));        corsConfiguration.setAllowedMethods(Arrays.asList("*"));        corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:8081"));        corsConfiguration.setMaxAge(3600L);        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();        source.registerCorsConfiguration("/**", corsConfiguration);        registrationBean.setFilter(new CorsFilter(source));        registrationBean.setOrder(-1);        return registrationBean;    }}
复制代码


  1. 手动创建 CorsConfiguration 对象

  2. 创建 UrlBasedCorsConfigurationSource 对象,将过滤器的拦截规则和 CorsConfiguration 对象之间的映射关系由 UrlBasedCorsConfigurationSource 中的 corsConfiguration 变量保存起来。

  3. 最后创建 CorsFilter 设置优先级


CorsFilter 的 doFilterInternal 方法:


protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {    CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);    boolean isValid = this.processor.processRequest(corsConfiguration, request, response);    if (isValid && !CorsUtils.isPreFlightRequest(request)) {        filterChain.doFilter(request, response);    }}
复制代码


触发对 CorsProcessor 的 processRequest 方法调用,跨域请求的校验工作将在该方法中完成

发布于: 刚刚阅读数: 4
用户头像

还未添加个人签名 2020.02.29 加入

公众号《盼盼小课堂》,多平台优质博主

评论

发布
暂无评论
跨域问题之Spring的跨域的方案_7月月更_周杰伦本人_InfoQ写作社区