CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(中)

简介: CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(中)

Spring MVC处理CORS请求的流程


Spring MVC处理任何一个reuqest请求都会去找到它的一个处理器Handler,因此首当其冲就来到DispatcherServlet#getHandler()这个方法~


getHandler()

对于Spring MVC来说,每处理一个request请求都应该对应着一个Handler:就是DispatcherServlet.getHandler()方法来找到其对应的处理器:


DispatcherServlet:
  // 根据HttpRequest从handlerMappings找到对应的handler
  @Nullable
  protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
      // 开启Spring MVC后默认情况下handlerMappings的长度是4
      for (HandlerMapping mapping : this.handlerMappings) {
        HandlerExecutionChain handler = mapping.getHandler(request);
        if (handler != null) {
          return handler;
        }
      }
    }
    return null;
  }


handlerMappings它的长度默认是3,内容如下:


image.png


处理本例请求的是RequestMappingHandlerMapping,获取处理器的方法在父类上:


AbstractHandlerMapping:
  // 默认使用的是UrlBasedCorsConfigurationSource来管理跨域配置
  private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
  // 使用的都是本类的pathMatcher和urlPathHelper
  public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
    Assert.notNull(corsConfigurations, "corsConfigurations must not be null");
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.setCorsConfigurations(corsConfigurations);
    source.setPathMatcher(this.pathMatcher);
    source.setUrlPathHelper(this.urlPathHelper);
    this.corsConfigurationSource = source;
  }
  // @since 5.1 此方法出现较晚,但一般也不建议去设置
  public void setCorsConfigurationSource(CorsConfigurationSource corsConfigurationSource) {
    Assert.notNull(corsConfigurationSource, "corsConfigurationSource must not be null");
    this.corsConfigurationSource = corsConfigurationSource;
  }
  @Override
  @Nullable
  public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // getHandlerInternal这个方法是根据URL去匹配一个Handler,当然有可能是匹配不上的,那么handler就为null
    Object handler = getHandlerInternal(request);
    if (handler == null) {
      handler = getDefaultHandler();
    }
    // 若最终还是为null,那就返回null 后续的也就不再处理了
    // 它的结果是:交给下一个HandlerMapping处理,若所有的处理完后还是返回null。
    // 那就noHandlerFound(processedRequest, response) --> 404
    if (handler == null) { 
      return null;
    }
    ...
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    ...
    // 若是跨域请求,这里就继续处理,也是本文讲述具有差异性的地方所在
    if (CorsUtils.isCorsRequest(request)) {
      // 1、全局配置:从UrlBasedCorsConfigurationSource找到一个属于这个请求的配置
      // 请注意:若三种方式都没有配置,这里返回的就是null~~~
      CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
      // 2、从handler自己里找:若handler自己实现了CorsConfigurationSource接口,那就从自己这哪呗
      // 说明:此种方式适用于一个类就是一个处理器的case。比如servlet处理器
      // 所以对于@RequestMapping情况,这个值大部分情况都是null
      CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      // 3、把全局配置和handler配置combine组合合并
      CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
      // 4、这个方法很重要。请看下面这个方法
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }
  }
  // @since 4.2
  protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request, HandlerExecutionChain chain, @Nullable CorsConfiguration config) {
    // 若是预检请求:就new一个新的HandlerExecutionChain。
    // PreFlightHandler是一个HttpRequestHandler哦~~~并且实现了接口CorsConfigurationSource
    if (CorsUtils.isPreFlightRequest(request)) {
      HandlerInterceptor[] interceptors = chain.getInterceptors();
      chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
    }
    // 若不是预检请求,就添加一个拦截器CorsInterceptor
    // 注意:这个拦截器只会作用于这个chain哦(也就是这个handler~~~) 
    // 能进来这里是简单请求 或者 真实请求。
    else {
      chain.addInterceptor(new CorsInterceptor(config));
    }
    return chain;
  }


根据URL成功匹配到一个Handler后,若是跨域请求就会继续添加跨域部分的处理逻辑:


  • 若是预检请求:针对此请求会直接new一个PreFlightHandler作为HttpRequestHandler处理器来处理它,而不再是交给匹配上的Handler去处理(这点特别的重要)

   - PreFlightHandler#handle方法委托给了corsProcessor去处理跨域请求头、响应头的

    - 值得注意的是:此时即使原Handler它不执行了,但匹配上的HandlerInterceptor们仍都还是会生效执行作用在OPTIONS方法上的

  • 若是简单请求/真实请求:在原来的处理链上加一个拦截器chain.addInterceptor(new CorsInterceptor(config)),由这个拦截器它最终复杂来处理相关逻辑(全权委托给corsProcessor)


核心的处理步骤就这么简单,理解起来也并不困难。因此我们还非常有必要的就是这三种配置方式是如何被初始化的呢?


CorsFilter方式初始化


要让它生效就需要我们手动把它注册进Servlet容器内,由它“拦截请求”自己来完成CorsProcessor.processRequest(corsConfiguration, request, response)这些处理操作。所以它和后续的getHandler()等这些处理逻辑是关系不大的。

此种方式的优雅程度上和自己实现差异并不大,因此我个人是不太推荐的~~


WebMvcConfigurer.addCorsMappings()方式初始化


这种方式是我推荐的,它的基本原理和我之前说过的WebMvcConfigurer其它配置项差不多。它作用的地方就是下面我列出的4个HandlerMapping初始化的时候。


WebMvcConfigurationSupport:
  @Bean
  public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    ...
  }
  // 最终返回的是个SimpleUrlHandlerMapping 可以直接完成映射
  @Bean
  @Nullable
  public HandlerMapping viewControllerHandlerMapping() {
    ViewControllerRegistry registry = new ViewControllerRegistry(this.applicationContext);
    ... 
  }
  // 按照bean名称进行匹配处理器
  @Bean
  public BeanNameUrlHandlerMapping beanNameHandlerMapping() {}
  // 最终也是个SimpleUrlHandlerMapping 
  @Bean
  @Nullable
  public HandlerMapping resourceHandlerMapping() {}


他们四个初始化时最终都调用了同一个方法:mapping.setCorsConfigurations(getCorsConfigurations())设置CORS配置,此方法是父类AbstractHandlerMapping提供的,原理可参考CorsRegistry和CorsRegistration .



相关文章
|
13天前
|
安全 API PHP
PHP中实现CORS跨域资源共享的方法
通过这种方式,你可以在PHP应用中灵活地实现CORS,以支持跨域Web应用的需求。
100 15
|
15天前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
8月前
|
前端开发 Java 测试技术
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
本文介绍了 `@RequestParam` 注解的使用方法及其与 `@PathVariable` 的区别。`@RequestParam` 用于从请求中获取参数值(如 GET 请求的 URL 参数或 POST 请求的表单数据),而 `@PathVariable` 用于从 URL 模板中提取参数。文章通过示例代码详细说明了 `@RequestParam` 的常用属性,如 `required` 和 `defaultValue`,并展示了如何用实体类封装大量表单参数以简化处理流程。最后,结合 Postman 测试工具验证了接口的功能。
410 0
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestParam
|
8月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestBody
`@RequestBody` 是 Spring 框架中的注解,用于将 HTTP 请求体中的 JSON 数据自动映射为 Java 对象。例如,前端通过 POST 请求发送包含 `username` 和 `password` 的 JSON 数据,后端可通过带有 `@RequestBody` 注解的方法参数接收并处理。此注解适用于传递复杂对象的场景,简化了数据解析过程。与表单提交不同,它主要用于接收 JSON 格式的实体数据。
633 0
|
8月前
|
前端开发 Java 微服务
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@PathVariable
`@PathVariable` 是 Spring Boot 中用于从 URL 中提取参数的注解,支持 RESTful 风格接口开发。例如,通过 `@GetMapping(&quot;/user/{id}&quot;)` 可以将 URL 中的 `{id}` 参数自动映射到方法参数中。若参数名不一致,可通过 `@PathVariable(&quot;自定义名&quot;)` 指定绑定关系。此外,还支持多参数占位符,如 `/user/{id}/{name}`,分别映射到方法中的多个参数。运行项目后,访问指定 URL 即可验证参数是否正确接收。
399 0
|
4月前
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
272 0
|
4月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
133 0
|
4月前
|
JSON 前端开发 Java
第05课:Spring Boot中的MVC支持
第05课:Spring Boot中的MVC支持
207 0
|
6月前
|
人工智能 前端开发 JavaScript
webpack-dev-server代理后端一直报CORS跨域或500错误
在Vue项目中使用Webpack的devServer代理后端接口时,遇到500错误。问题根源在于浏览器请求中携带的Origin头导致服务器报错,而Postman测试正常。通过分析发现,调整或移除Origin头可解决问题。解决办法包括:1) 在代理配置中添加正确的Origin头;2) 删除请求中的Origin头。文章还深入解析了Origin头的作用及changeOrigin配置的实际意义,并附带相关文档链接,帮助开发者更好地理解与解决类似跨域问题。
389 20
|
8月前
|
前端开发 JavaScript 应用服务中间件
前端跨域问题解决Access to XMLHttpRequest at xxx from has been blocked by CORS policy
跨域问题是前端开发中常见且棘手的问题,但通过理解CORS的工作原理并应用合适的解决方案,如服务器设置CORS头、使用JSONP、代理服务器、Nginx配置和浏览器插件,可以有效地解决这些问题。选择合适的方法可以确保应用的安全性和稳定性,并提升用户体验。
4911 90

热门文章

最新文章

下一篇
开通oss服务