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

复制代码
相关推荐
Xudde.35 分钟前
HTML中最基本的东西
前端·css·笔记·html
杨荧2 小时前
【开源免费】基于Vue和SpringBoot的林业产品推荐系统(附论文)
前端·javascript·vue.js·spring boot·开源
光影少年2 小时前
前端进程和线程及介绍
前端·javascript
Franciz小测测2 小时前
VUE3 + Ant Design Vue4 开发笔记
前端·vue.js·vue
乌龟的黑头-阿尔及利亚3 小时前
使用 Vite 创建 Vue 3 项目:从零开始的详细指南
前端·javascript·vue.js
三天不学习3 小时前
what?ngify 比 axios 更好用,更强大?
前端·axios·请求响应·ngify
2403_875180953 小时前
一键掌握多平台短视频矩阵营销/源码部署
java·前端·数据结构·线性代数·矩阵·php
Best_卡卡3 小时前
前端性能-HTTP缓存
前端·http·缓存