CORS跨域资源共享(二):详解Spring MVC对CORS支持的相关类和API【享学Spring MVC】(上)

简介: CORS跨域资源共享(二):详解Spring MVC对CORS支持的相关类和API【享学Spring MVC】(上)

前言


上篇文章通过我模拟的跨域请求实例和结果分析,相信小伙伴们都已经80%的掌握了CORS到底是怎么一回事以及如何使用它。由于Java语言中的web框架几乎都是使用的Spring MVC,因此本文将聚焦于Spring MVC对CORS的支持,深度分析下它对CORS支持的相关API,这也方便下一章节的灵活使用以及流程原理分析。


Spring MVC与CORS


Spring MVC一直到4.2版本“才”开始内置对CORS支持,至于为何到这个版本Spring官方才对此提供支持,我这里需要结合时间轴来给大家解释一下。

上文我有说到了CORS它属于W3C的标准。我们知道任何一个规范的形成都是非常漫长的。W3C对web标准的制定分为如下7个阶段(从上到下有序):


  1. WD(Working Draft 工作草案):不稳定也不完整
  2. CR(Candidate Recommendation 候选推荐标准):所有的已知issues都被解决了
  3. PR(Proposed Recommendation 提案推荐标准):在浏览器做各种测试,此部分不会再有实质性的改动
  4. PER(Proposed Edited Recommendation 已修订的提案推荐标准):
  5. REC(Recommendation 推荐标准,通常称之为 standard,即事实标准):几乎不会再变动任何东西
  6. RET(Retired 退役的):最后这两个是建立在REC基础上变来,成熟的技术一般都不会有后面这两个
  7. NOTE(Group Note 工作组说明):


关于这7步,从这里 可以看倒CORS的WD从2009-03-17开始,2014-01-16进入的REC阶段,可谓正式毕业。而Spring4.2是在2015-06发布给与的全面支持,从时间轴上看Spring的响应速度还是把握得不错的(毕竟CORS经历过一段时间市场的考验Spring才敢全面纳入进来支持嘛~)


Tips:在Spring4.2之前,官方没有提供内置的支持,所以那时都是自己使用Filter/拦截器来处理。它的唯一缺点就是可能没那么灵活和优雅,后续官方提供标注支持后能力更强更为灵活了(底层原理都一样)


Spring MVC中CORS相关类及API说明


所有涉及到和CORS相关的类、注解、代码片段都是Spring4.2后才有的,请保持一定的版本意识。


image.png


从截图里可以看出spring-web包提供的专门用于处理CORS的相关的类,下面有必要进行逐个分析

CorsConfiguration


它代表一个cors配置,记录着各种配置项。它还提供了检查给定请求的实际来源、http方法和头的方法供以调用。用人话说:它就是具体封装跨域配置信息的pojo。

默认情况下新创建的CorsConfiguration它是不允许任何跨域请求的,需要你手动去配置,或者调用applyPermitDefaultValues()开启GET、POST、Head的支持~


几乎所有场景,创建完CorsConfiguration最后都调用了applyPermitDefaultValues()方法。也就是说你不干预的情况下,一个CorsConfiguration配置一般都是支持GET、POST、Head的


// @since 4.2
public class CorsConfiguration {
  // public的通配符:代表所有的源、方法、headers...
  // 若你需要使用通配符,可以使用此静态常量
  public static final String ALL = "*";
  private static final List<HttpMethod> DEFAULT_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.GET, HttpMethod.HEAD));
  // 默认许可所有方法
  private static final List<String> DEFAULT_PERMIT_ALL = Collections.unmodifiableList(Arrays.asList(ALL));
  // 默认许可这三个方法
  private static final List<String> DEFAULT_PERMIT_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name()));
  // ==========把这些属性对应上文讲述的响应头们对应,和W3C标注都是对应上的=========
  @Nullable
  private List<String> allowedOrigins;
  @Nullable
  private List<String> allowedMethods;
  @Nullable
  private List<HttpMethod> resolvedMethods = DEFAULT_METHODS;
  @Nullable
  private List<String> allowedHeaders;
  @Nullable
  private List<String> allowedHeaders;
  @Nullable
  private List<String> exposedHeaders;
  @Nullable
  private Boolean allowCredentials;
  @Nullable
  private Long maxAge;
  ... // 省略所有构造函数以及所有的get/set方法
  // 使用此方法将初始化模型翻转为以允许get、head和post请求的所有跨源请求的打开默认值开始
  // 注意:此方法不会覆盖前面set进去的值,所以建议此方法可以作为兜底调用。实际上Spring内部也是用它兜底的
  public CorsConfiguration applyPermitDefaultValues() {
    if (this.allowedOrigins == null) {
      this.allowedOrigins = DEFAULT_PERMIT_ALL;
    }
    if (this.allowedMethods == null) {
      this.allowedMethods = DEFAULT_PERMIT_METHODS;
      this.resolvedMethods = DEFAULT_PERMIT_METHODS.stream().map(HttpMethod::resolve).collect(Collectors.toList());
    }
    if (this.allowedHeaders == null) {
      this.allowedHeaders = DEFAULT_PERMIT_ALL;
    }
    if (this.maxAge == null) {
      this.maxAge = 1800L;
    }
    return this;
  }
  public CorsConfiguration combine(@Nullable CorsConfiguration other) { ... }
  // 根据配置的允许来源检查请求的来源
  // 返回值并不是bool值,而是字符串--> 返回可用的origin。若是null表示请求的origin不被支持
  @Nullable
  public String checkOrigin(@Nullable String requestOrigin) { ... }
  // 检查预检请求的Access-Control-Request-Method这个请求头
  public List<HttpMethod> checkHttpMethod(@Nullable HttpMethod requestMethod) { ... }
  // 检查预检请求的Access-Control-Request-Headers
  @Nullable
  public List<String> checkHeaders(@Nullable List<String> requestHeaders) {
}


这个POJO的配置,是servlet传统web以及reactive web所共用的,它提供有校验的基本方法。它的属性、校验原则和W3C的CORS标准所对应。


CorsConfigurationSource


它表示一个源,该接口主要是为请求提供一个CorsConfiguration。


public interface CorsConfigurationSource {
  // 找到此request的一个CORS配置
  @Nullable
  CorsConfiguration getCorsConfiguration(HttpServletRequest request);
}


此接口方法的调用处有三个地方:


  • AbstractHandlerMapping.getHandler()/getCorsConfiguration()
  • CorsFilter.doFilterInternal()
  • HandlerMappingIntrospector.getCorsConfiguration()


因为它可以根据request返回一个CORS配置。可以把这个接口理解为:存储request与跨域配置信息的容器。它的继承树如下:


image.png

首先需要说的便是cors包下的UrlBasedCorsConfigurationSource


UrlBasedCorsConfigurationSource


它位于org.springframework.web.cors包:它里面存储着path patterns和CorsConfiguration的键值对。


// @since 4.2
public class UrlBasedCorsConfigurationSource implements CorsConfigurationSource {
  // 请务必注意:这里使用的是LinkedHashMap
  private final Map<String, CorsConfiguration> corsConfigurations = new LinkedHashMap<>();
  private PathMatcher pathMatcher = new AntPathMatcher();
  private UrlPathHelper urlPathHelper = new UrlPathHelper();
  ... // 生路所有的get/set方法
  // 这里的path匹配用到的是AntPathMatcher.match(),默认是按照ant风格进行匹配的
  @Override
  @Nullable
  public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    for (Map.Entry<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) {
      if (this.pathMatcher.match(entry.getKey(), lookupPath)) {
        return entry.getValue();
      }
    }
    return null;
  }
}


本类它是作为AbstractHandlerMapping(RequestMappingHandlerMapping)的默认跨域资源配置的管理类

相关文章
|
23天前
|
API 索引
String类下常用API
String类下常用API
32 1
|
16天前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
47 0
|
8天前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
6天前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
1月前
|
Java 索引
|
24天前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
2月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
2月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
95 0
|
16天前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
26 0
|
1月前
|
XML Java 数据库连接
Spring Boot集成MyBatis
主要系统的讲解了 Spring Boot 集成 MyBatis 的过程,分为基于 xml 形式和基于注解的形式来讲解,通过实际配置手把手讲解了 Spring Boot 中 MyBatis 的使用方式,并针对注解方式,讲解了常见的问题已经解决方式,有很强的实战意义。在实际项目中,建议根据实际情况来确定使用哪种方式,一般 xml 和注解都在用。