SpringMvc解决跨域问题的源码汇总。

看本文章前,需了解跨域的缘由

其次,了解RequestMapping的基础原理

最后我们来解析SpringMvc是如何处理跨域问题的。

跨域信息配置

SpringMvc分为全局级别和局部级别两种,全局级别就是任何跨域请求都起作用。

全局级别

全局级别就是在配置HandlerMapping时,从源码分析可知,AbstractHandlerMapping是requestMapping的第一子类,其内部拥有属性globalCorsConfigSource,这个属性对应的对象是一个实现了org.springframework.web.cors.CorsConfigurationSource的对象,通过暴露org.springframework.web.servlet.handler.AbstractHandlerMapping#setCorsConfigurations方法,可以直接向globalCorsConfigSource注入url与跨域配置信息。至于该方法如何调用,则是在Springboot里有所体现。

局部级别

而局部级别的,就是针对request的url对应的特定handler起作用。从RequestMapping的查找我们可以得知,springmvc在获取handler的过程中,有两种解析方式,一种是对象级别的,一种是方法级别的。

针对对象级别的

SpringMvc提供了org.springframework.web.cors.CorsConfigurationSource接口,也就是说,如果你的handler实现了org.springframework.web.cors.CorsConfigurationSource,那么当有跨域出现的请求时,会优先使用CorsConfigurationSource的实现来实现。

针对方法级别的

优先使用在方法上注解的org.springframework.web.bind.annotation.CrossOrigin,其次使用所属bean上注解的org.springframework.web.bind.annotation.CrossOrigin。它们会将这个注解信息解析成CorsConfigurationSource的子类。

如何处理跨域

首先,根据浏览器跨域规则我们可以知道,对于跨域资源,浏览器会有两种行为。

一种行为是,假如是一个简单的Get跨域请求,浏览器将直接发起请求。

另外一种行为是,假如是一个使用 PUTDELETE 方法的请求,浏览器会发送一个预检请求,所谓的预检请求,指的就是发送一个请求方法为OPTIONS的请求。

针对这两种行为,SpringMvc有不同的处理方式。

OPTIONS请求方法

针对OPTION的跨域请求方法,springMvc底层会默认匹配一个PreFlightHandler的handler,这个handler作用也很简单,就是查找上面的CorsConfigurationSource,获取这个url匹配的跨域配置CorsConfiguration,并且查看做如下匹配

1.请求的Request的Origin头的值,是否与CorsConfiguration里的allowedOrigins里匹配,匹配则放行。

2.再次检查Access-Control-Request-Method头的值,是否与CorsConfiguration里allowedMethods是否匹配,匹配则放行。

3.再次检查Access-Control-Request-Headers头的值,是否与CorsConfiguration里allowedHeaders是否匹配,匹配则放行。

如果上面任意一步无法做到匹配,就会直接返回一个Response,状态码为403,而值的信息Invalid CORS request。

如果以上都通过了,则直接会设置如下的相应头:

复制代码
Access-Control-Allow-Origin:即Request请求里的Origin头
复制代码
Access-Control-Allow-Methods:即Request里的Access-Control-Request-Method对应的值,且该值必须被跨域允许的,比如,传了GET、POST,但是跨域配置里,即CorsConfiguration的allowedMethods为GET,则该值就会是GET
复制代码
Access-Control-Allow-Headers:即Request里的Access-Control-Request-Headers对应的值,且该值必须被跨域允许的,比如,传了COOKIE、TOKEN等,但是跨域配置里,即CorsConfiguration的allowedHeaders为TOKEN,则该值就会是TOKEN
复制代码
Access-Control-Expose-Headers:即根据跨域配置里,即CorsConfiguration的exposedHeaders的值,这个值的作用是什么呢?那就是告诉浏览器,你接到这个相应的时候,可以暴露哪些头信息,比如自定义了一个Token头,但是如果你不指定的话,浏览器是不会展示这个头给用户的,即便,你在响应里设置了这个头。
复制代码
Access-Control-Allow-Credentials:该值是告诉浏览器,是否允许发送敏感信息,这些敏感信息包含cookie、TLS 客户端证书,或包含用户名和密码的认证标头等。为true则是允许。

Access-Control-Max-Age:用于指定预检请求(preflight request)的结果可以缓存多长时间,以减少浏览器发送预检请求的次数,从而提升跨域请求的性能。

直接请求方法

对于非预检请求,springMvc会为这个handler生成一个HandlerInterceptor,即org.springframework.web.servlet.handler.AbstractHandlerMapping.CorsInterceptor。

这个拦截器的org.springframework.web.cors.DefaultCorsProcessor#processRequest里对请求是否跨域进行处理。

1.请求的Request的Origin头的值,是否与CorsConfiguration里的allowedOrigins里匹配,匹配则放行。

2.再次检查Access-Control-Request-Method头的值,是否与CorsConfiguration里allowedMethods是否匹配,匹配则放行。

如果以上都通过了,则直接会设置如下的相应头:

复制代码
Access-Control-Allow-Origin:即Request请求里的Origin头
复制代码
Access-Control-Expose-Headers:即根据跨域配置里,即CorsConfiguration的exposedHeaders的值,这个值的作用是什么呢?那就是告诉浏览器,你接到这个相应的时候,可以暴露哪些头信息,比如自定义了一个Token头,但是如果你不指定的话,浏览器是不会展示这个头给用户的,即便,你在响应里设置了这个头。
复制代码
Access-Control-Allow-Credentials:该值是告诉浏览器,是否允许发送敏感信息,这些敏感信息包含cookie、TLS 客户端证书,或包含用户名和密码的认证标头等。为true则是允许。

如果以上有任意匹配不通过,则直接通过拦截器返回CorsInterceptor返回false,即不再继续处理请求。

CorsConfiguration

从上面我们可以知道,无论你用那种方法,跨域的配置信息始终存放在org.springframework.web.cors.CorsConfigurationSource的子类中,而这个类SpringMvc默认提供的实现是org.springframework.web.cors.UrlBasedCorsConfigurationSource。org.springframework.web.cors.UrlBasedCorsConfigurationSource则是非常简单,就是根据url查找到对应的CorsConfiguration对象。

复制代码
CorsConfiguration内部存储着如下信息:
复制代码
allowedOrigins:即它指定了跨域Request的Access-Control-Allow-Origin的头信息,如果这个属性里不包含Access-Control-Allow-Origin,就代表跨域不允许。
复制代码
allowedMethods:即它指定了跨域Request的Access-Control-Request-Headers的头信息,如果这个属性里不包含Access-Control-Request-Headers,就代表跨域不允许。
复制代码
allowedHeaders:即它指定了跨域Request的Access-Control-Request-Method的头信息,如果这个属性里不包含Access-Control-Request-Method,就代表跨域不允许

exposedHeaders:Access-Control-Expose-Headers

复制代码
相关推荐
前端付豪几秒前
美团 ETA 模型在线训练与推理加速实践
前端·后端·架构
阿慧勇闯大前端1 分钟前
你真的搞懂JavaScript中的异步了吗?
前端
前端小嘎1 分钟前
告别繁琐,拥抱自由:使用 Sanity.io 轻松搭建你的现代化博客
前端
tkt4 分钟前
Android Compose 中,@Immutable 和 @Stable 的理解
前端
qq_12498707535 分钟前
基于node.js+vue的医院陪诊系统的设计与实现(源码+论文+调试+安装+售后)
前端·vue.js·node.js·毕业设计
markyankee1015 分钟前
JavaScript中的this关键字:全面解析与实战指南
前端·javascript
去伪存真6 分钟前
如何集中管理多个项目的Jenkins Job 流程?
前端·jenkins
邹荣乐7 分钟前
uni-app多端应用开发:常见跨端兼容问题及处理策略
前端·微信小程序·uni-app
前端达人7 分钟前
React Router 是怎么实现灵活导航的?
前端·javascript·react.js·前端框架·ecmascript
摘取一颗天上星️16 分钟前
Electron架构深度解析:用Web技术构建桌面应用的桥梁
前端·架构·electron