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 .



相关文章
|
8天前
|
设计模式 前端开发 Java
步步深入SpringMvc DispatcherServlet源码掌握springmvc全流程原理
通过对 `DispatcherServlet`源码的深入剖析,我们了解了SpringMVC请求处理的全流程。`DispatcherServlet`作为前端控制器,负责请求的接收和分发,处理器映射和适配负责将请求分派到具体的处理器方法,视图解析器负责生成和渲染视图。理解这些核心组件及其交互原理,有助于开发者更好地使用和扩展SpringMVC框架。
23 4
|
22天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
48 14
|
25天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
78 2
|
1月前
|
JavaScript 前端开发 API
跨域资源共享(CORS)的工作原理是什么?
跨域资源共享(CORS)通过浏览器和服务器之间的这种交互机制,在保证安全性的前提下,实现了跨域资源的访问,使得不同源的网页能够合法地获取和共享服务器端的资源,为现代Web应用的开发提供了更大的灵活性和扩展性。
|
20天前
|
安全 Java API
实现跨域请求:Spring Boot后端的解决方案
本文介绍了在Spring Boot中处理跨域请求的三种方法:使用`@CrossOrigin`注解、全局配置以及自定义过滤器。每种方法都适用于不同的场景和需求,帮助开发者灵活地解决跨域问题,确保前后端交互顺畅与安全。
|
2月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
2月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
624 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
2月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
64 2
|
2月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
229 2
|
2月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
189 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习