你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

RESTful开发风格8:跨域问题二:Spring MVC实现“CORS跨域访问”的两种策略:类上使用【@CrossOrigin注解】;配置文件中通过<mvc:cors>进行全局配置;

2021/11/18 5:36:15

说明:

(1)本篇博客的合理性解释:

          ● 上篇博客的脉络是:【RESTful开发风格中,访问远程网站肯定会经常遇到】→【但是,浏览器存在一个同源策略】→【即,当访问不同域的资源时,会触犯浏览器同源策略,出现无法访问的问题】→【那么,为了能够实现访问不同域中的资源,就需要一种“跨域访问”机制】→【所以,在介绍“跨域访问”之前,上篇博客就先介绍浏览器的同源策略】;

          ● 既然,上篇博客已经介绍了【浏览器同源策略】;

          ● 那么【为了能够在访问不同域中的资源时,不触犯浏览器同源策略】,本篇博客就来介绍【Spring MVC实现“跨域访问”的方法】;

(2)强调:本篇博客的CORS跨域资源访问的内容,仅仅适用于【客户端是HTML浏览器的情况】;如果【客户端是小程序或者App】,就需要考虑其他策略了;

(3)本篇博客的代码,延续自【RESTful开发风格6:RESTful基本使用四:JSON序列化;(jackson组件,以及其中的 @JsonFormat注解来解决时间问题;)】 中的【restful工程】;

目录

一:CORS跨域访问简介;

1.【CORS跨域访问】,机制简介;

2.Spring MVC,实现“CORS跨域访问”的方法;

二:项目准备;CORS跨域问题演示;

0.演示前准备:为了模拟“CORS跨域访问”:比照【restful工程】,创建【restful_8080工程】;

1. CORS跨域问题演示;

三:Spring MVC,实现“CORS跨域访问”的两种策略;

1.策略1:在服务提供端的类上使用【@CrossOrigin注解】;

(0)预先说明;

(1)在提供服务方的Controller上使用@CrossOrigin注解;

(3)可以跨域访问的:效果:

(4)原因解释,即为什么增加了【@CrossOrigin注解】就能解决跨域访问的问题嘞?  

(5)【@CrossOrigin注解】:设置多个域名;

(6)【@CrossOrigin注解】:对所有域名,放开跨域访问;(这种,在实际开发中,几乎不会用到,因为存在严重的安全隐患)

(7)【@CrossOrigin注解】:maxAge参数:设置预检请求的缓存时间;

2.策略2:在applicationContext.xml中,通过<mvc:cors>2.策略2:在applicationContext.xml中,通过

(0)预先说明;

(1)在applicationContext.xml中,通过标签进行一次性的全局配置;

(2)启动Tomcat,观察效果;

四:补充说明;

(1)上面的CORS跨域访问的内容:仅仅适用于【客户端是浏览器的情况】;

(2)【@CrossOrigin注解】和【全局配置】选用哪个?


 

一:CORS跨域访问简介;

1.【CORS跨域访问】,机制简介;

说明:

(0)CORS(Cross-origin resource sharing):跨域资源共享,又称跨域资源访问;

(1)CORS跨域访问是一种机制:

          ● 通过“跨域访问机制”,能够解决【当访问不同域的资源时,会触犯浏览器同源策略,从而出现无法访问的问题】;

          ● 这种机制是指:通过【在“HTTP请求”以及“响应头”的部分,附加一些额外的信息】,以通知浏览器可以访问其他域的资源;(自然,这些其他域的资源,在对应域名下也得是允许被访问的)

(2)CORS跨域访问机制中,我们附加了什么信息?:

          ● CORS如果要进行远程跨域访问,需要在url的响应头中包含【以Access-Control开头的响应头】,以指明当前请求是允许跨域访问的;

          ● 自然【Access-Control响应头】不是随随便便就能加上的,这需要远程服务器对应的资源进行相应的授权才可以;

2.Spring MVC,实现“CORS跨域访问”的方法;

说明:

(1)第一种方法:在类中使用【@CrossOrigin注解】,来说明当前Controller所映射的url允许被跨域访问;这个注解只在当前Controller中生效,即这种方法的作用范围是局部的;

如果我们系统中,有大量的Controller都要允许被跨域访问,就要考虑第二种方法了;

(2)第二种方法:在在applicationContext.xml配置文件中,通过<mvc:cors>标签进行一次性的全局配置;


二:项目准备;CORS跨域问题演示;

0.演示前准备:为了模拟“CORS跨域访问”:比照【restful工程】,创建【restful_8080工程】;

如有需要,创建过程可以参考【本专栏中,前面的博客】;这儿就不重复了;

但是需要注意两点:

……………………………………………………

即,我们创建的【restful_8080工程】的所有资源的域都是【http://localhost:8080】的;而,我们原先的【restful工程】的所有资源的域都是【http://localhost】(或者是【http://localhost:80】,因为我们设置了Tomcat默认端口是80,所以默认端口时80可以省略);

1. CORS跨域问题演示;

启动【restful_8080】工程:

即,在上面的过程中【localhost:8080,下的client.html】访问【localhost:8080,下的/restful/persons】时,没有跨域,其自然是可以访问的;

……………………………………………………

但是,如果修改下【restful_8080工程】的url:


三:Spring MVC,实现“CORS跨域访问”的两种策略;

1.策略1:在服务提供端的类上使用【@CrossOrigin注解】;

(0)预先说明;

          ● 因为在上面的案例中,是【restful_8080工程的:localhost:8080,下的client.html】访问【restful工程的:localhost:8080,下的/restful/persons】;即【restful工程】是服务提供端;

          ● 那么为了能够让【restful_8080工程,作为请求方】能够访问【restful工程,提供的服务】:需要在【restful工程,提供服务的Controller类上使用@CrossOrigin注解】;


(1)在提供服务方的Controller上使用@CrossOrigin注解;

说明:

          ● 使用【@CrossOrigin注解】来设置跨域的范围;即,说明【哪些其他域名下,送过来的请求】是允许访问本Controller提供的服务;

          ● 其中的origins配置上,每一条域名要写完整,即协议、域名、端口都要写上;


(3)可以跨域访问的:效果:

上面设置好了之后,重启【restful工程】和【restful_8080工程】:


(4)原因解释,即为什么增加了【@CrossOrigin注解】就能解决跨域访问的问题嘞?  

首先,如果请求发起方,如果其发起的请求是跨域请求,会在请求头中增加【Sec-Fetch-Mode: cors】; 

然后,如果请求接收方(就是服务器端),可以接受请求方的跨域访问,那么会在响应头中增加【Vary:Access-Controll】之类的信息;

………………………… 

反例:比如,我们没有通过【@CrossOrigin注解】设置跨域访问时:

 


(5)【@CrossOrigin注解】:设置多个域名;

直接增加就行了; 

@CrossOrigin(origins = {"http://localhost:8080","http://www.imooc.com"})


(6)【@CrossOrigin注解】:对所有域名,放开跨域访问;(这种,在实际开发中,几乎不会用到,因为存在严重的安全隐患)

在实际工作中,坚决不能这么干。


(7)【@CrossOrigin注解】:maxAge参数:设置预检请求的缓存时间;

【预检请求】回顾;

在【RESTful开发风格5:RESTful基本使用三:简单请求(GET和POST);非简单请求(PUT和DELETE);(包括FormContentFilter过滤器)】我们知道:

          ● 请求包括简单请求和非简单请求;

          ● PUT和DELETE请求是非简单请求;

          ● 而非简单请求包括【预检请求】和【正式请求】;

          ● 先发送【预检请求】,向服务器检查是否允许被访问;如果允许,再发送【正式请求】;如果不允许,当前操作就会被中断;

【预检请求】带来的问题:但是,上面的流程会产生一个问题;

          ● PUT和DELETE这些非简单请求在每一次发送时,都会有【预检请求】和【正式请求】这两个请求,而这必然会增加服务器的压力。

          ● 但是,在如此糟糕的情况下,还是有一个比较利好的情况,即【预检请求】的授权逻辑是不会轻易改变的。所以,maxAge参数,就派上用场了。

maxAge参数;

          ● 该参数表示,我们可以把【预检请求】的结果进行缓存;

          ● 比如【maxAge=3600】:设置预检请求的缓存时间为3600秒:表示:在一小时时间内,同样的PUT或DELETE等非简单请求,再次发送的时候,就不再需要进行预检请求处理了,直接发送实际请求。当一小时之后,再发送PUT或DELETE等非简单请求时,重新发送【预检请求】以重新检查服务器状态,如果预检请求通过,那么我们再次将其缓存一小时……

          ● 即,maxAge可以帮我们降低服务器压力;

          ● 一定要清楚:maxAge缓存的是【预检请求的处理结果】,而不是【请求的内容】;

【@CrossOrigin注解】虽然好用,但是如果Controller很多时,在很多Controller类上都使用【@CrossOrigin注解】还是比较麻烦的;为此,就引出了Spring MVC实现“跨域访问”的第二种策略:<mvc:cors>标签进行一次性的全局配置;

2.策略2:在applicationContext.xml中,通过<mvc:cors>标签进行一次性的全局配置;

(0)预先说明;

【@CrossOrigin注解】虽然简单,但是还是存在一些问题;

          ● 如果一个项目中,有很多Controller需要配置【@CrossOrigin注解】,就有可能出现遗漏的情况;

          ● 或者,当前系统要统一的设置跨域的域名时,使用【@CrossOrigin注解】就会略显笨拙;

          ● 我们需要一个可以全局配置的方式,为此就引出了【<mvc:cors>标签进行一次性的全局配置】;

(1)在applicationContext.xml中,通过<mvc:cors>标签进行一次性的全局配置;

    <mvc:cors>
        <mvc:mapping path="/restful/**"
                     allowed-origins="http://localhost:8080,http://www.imooc.com"
                     max-age="3600"/>
    </mvc:cors>

(2)启动Tomcat,观察效果;


 

四:补充说明;

(1)上面的CORS跨域访问的内容:仅仅适用于【客户端是浏览器的情况】;

CORS跨域资源访问的上面两种策略:【第一种方法:在类中使用:@CrossOrigin注解】和【第二种方法:在在applicationContext.xml配置文件中,通过<mvc:cors>标签进行全局配置】:只是在浏览器中的策略;

如果客户端不是HTML浏览器,而是小程序或者APP的话,上面的策略是不行的;

(2)【@CrossOrigin注解】和【<mvc:cors>全局配置】选用哪个?

          ● 如果,当前应用是一个【专用的webAPI】(即,是一个只对外提供web数据服务的应用),就需要使用【<mvc:cors>全局配置】的方式;

          ● 如果,只是个别的Controller需要对外暴露服务,就推荐使用【@CrossOrigin注解】方式;

          ● 如果,一个项目既使用了【<mvc:cors>全局配置】,又使用了【@CrossOrigin注解】:在实际运行时候,会以【@CrossOrigin注解】为准;