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

复制代码
相关推荐
树上有只程序猿3 分钟前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼38 分钟前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下1 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox1 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞1 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行1 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758101 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周1 小时前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队1 小时前
Vue自定义指令最佳实践教程
前端·vue.js
Jasmin Tin Wei2 小时前
蓝桥杯 web 学海无涯(axios、ecahrts)版本二
前端·蓝桥杯