返回介绍

11.2 Spring 处理方案

发布于 2025-04-26 13:16:47 字数 5613 浏览 0 评论 0 收藏

首先回顾一下 Spring 中关于跨域的处理方案,这有助于我们理解 Spring Security 中的跨域。Spring 中对于跨域的处理一共有三种不同的方案,我们分别来看。

11.2.1 @CrossOrigin

Spring 中第一种处理跨域的方式是通过 @CrossOrigin 注解来标记支持跨域,该注解可以添加在方法上,也可以添加在 Controller 上。当添加在 Controller 上时,表示 Controller 中的所有接口都支持跨域,具体配置如下:

    @RestController
    public class HelloController {
       @CrossOrigin(origins = "http://localhost:8081")
       @PostMapping("/post")
       public String post() {
           return "hello post";
       }
    }

@CrossOrigin 注解各属性含义如下:

- allowCredentials:浏览器是否应当发送凭证信息,如 Cookie。

- allowedHeaders:请求被允许的请求头字段,*表示所有字段。

- exposedHeaders:哪些响应头可以作为响应的一部分暴露出来。注意,这里只可以一一列举,通配符*在这里是无效的。

- maxAge:预检请求的有效期,有效期内不必再次发送预检请求,默认是 1800 秒。

- methods:允许的请求方法,*表示允许所有方法。

- origins:允许的域,*表示允许所有域。

该注解的实现原理属于 Spring 范畴,这里不做过多介绍,本书仅和大家简单梳理一下 @CrossOrigin 注解执行过程,这有助于我们理解后面的内容(读者可按照下面的叙述自行 Debug 以加深理解)。

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

(2)将 @CrossOrigin 所标记的请求方法对象 HandlerMethod 和 CorsConfiguration 一一对应存入一个名为 corsLookup 的 Map 集合中。

(3)当请求到达 DispatcherServlet#doDispatch 方法之后,调用 AbstractHandlerMapping#getHandler 方法获取执行链 HandlerExecutionChain 时,会从 corsLookup 集合中获取到 CorsConfiguration 对象。

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

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

11.2.2 addCorsMappings

@CrossOrigin 注解需要添加在不同的 Controller 上。所以还有一种全局的配置方法,就是通过重写 WebMvcConfigurerComposite#addCorsMappings 方法来实现,具体配置如下:

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

addMapping 表示要处理的请求地址,接下来的方法含义和 @CrossOrigin 注解中属性的含义都一一对应,这里不再赘述。

这种配置方式最终的处理方式和 @CrossOrigin 注解相同,都是在 CorsInterceptor 拦截器中触发对 DefaultCorsProcessor#processRequest 的调用,并最终在该方法中完成对跨域请求的校验工作。不过在源码执行过程中略有差异。

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

(2)在 WebMvcConfigurationSupport#requestMappingHandlerMapping 方法中触发了 addCorsMappings 方法的执行,将获取到的 CorsRegistration 对象重新组装成一个 UrlBasedCors ConfigurationSource 对象,该对象中定义了一个 corsConfigurations 变量(Map<String,CorsConfiguration>),该变量保存了拦截规则和 CorsConfiguration 对象的映射关系。

(3)将新建的 UrlBasedCorsConfigurationSource 对象赋值给 AbstractHandlerMapping#cors ConfigurationSource 属性。

(4)当请求到达时的处理方法和 @CrossOrigin 注解处理流程的第 3 步一样,都是在 AbstractHandlerMapping#getHandler 方法中进行处理,不同的是,这里是从 corsConfiguration Source 中获取 CorsConfiguration 配置对象,而 @CrossOrigin 注解则从 corsLookup 集合中获取到 CorsConfiguration 配置对象。如果两处都可以获取到 CorsConfiguration 对象,则对获取到的对象属性值进行合并。

(5)根据获取到的 CorsConfiguration 对象构建一个 CorsInterceptor 拦截器。

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

这两种跨域配置方式殊途同归,最终目的都是配置了一个 CorsConfiguration 对象,并根据该对象创建 CorsInterceptor 拦截器,然后在 CorsInterceptor 拦截器中触发 DefaultCorsProcessor#processRequest 方法的执行,完成跨域的校验。另外还需要注意的是,这里的跨域校验是由 DispatcherServlet 中的方法触发的,而 DispatcherServlet 的执行是在 Filter 之后,这一点需要牢记,我们后面将会用到。

11.2.3 CorsFilter

CorsFilter 是 Spring Web 中提供的一个处理跨域的过滤器,开发者也可以通过该过滤器处理跨域:

    @Configuration
    public 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;
       }
    }

CorsFilter 过滤器的配置也很容易:

- 由于是在 Spring Boot 项目中,这里通过 FilterRegistrationBean 来配置一个过滤器,这种配置方式既可以设置拦截规则,又可以为配置的过滤器设置优先级。

- 在这里依然离不开 CorsConfiguration 对象,不同的是我们自己手动创建该对象,并逐个设置跨域的各项处理规则。

- 我们还需要创建一个 UrlBasedCorsConfigurationSource 对象,将过滤器的拦截规则和 CorsConfiguration 对象之间的映射关系由 UrlBasedCorsConfigurationSource 中的 corsConfigurations 变量保存起来。

- 最后创建一个 CorsFilter,并为其配置一个优先级。

在 CorsFilter 过滤器的 doFilterInternal 方法中,触发对 DefaultCorsProcessor#processRequest 的调用,进而完成跨域请求的校验。

和前面两种方式不同的是,CorsFilter 是在过滤器中处理跨域的,而前面两种方案则是在 DispatcherServlet 中触发跨域处理,从处理时间上来说,CorsFilter 对于跨域的处理时机要早于前面两种。

这就是 Spring 中为我们提供的三种不同的跨域解决方案,三种方式都能解决问题,选择其中任意一种即可。需要说明的是:

- @CrossOrigin 注解 + 重写 addCorsMappings 方法同时配置,这两种方式中关于跨域的配置会自动合并,跨域在 CorsInterceptor 中只处理了一次。

- @CrossOrigin 注解 + CorsFilter 同时配置,或者重写 addCorsMappings 方法 + CorsFitler 同时配置,都会导致跨域在 CorsInterceptor 和 CorsFilter 中各处理了一次,降低程序运行效率,这种组合不可取。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。