同源策略
浏览器同源策略:同源策略(Same origin policy)是一种约定,它是浏览器最核心也是最基本的安全功能。出于安全考虑,浏览器限制从JS脚本发起的跨源HTTP请求。
同源策略中的"源"是三个元素的组合:模式(协议),主机名(域/子域)和端口。
域名、协议、端口有一个不同就不是同源,三者均相同,这两个网站才是同源
注1:同源策略只针对于浏览器端,浏览器一旦检测到请求的结果的域名不一致后,会堵塞请求结果。这里注意,跨域请求是可以发去的,但是请求响应response被浏览器堵塞了。
注2:同源策略的非绝对性:<script> <img> <iframe> <link> <video> <audio>
等带有src属性的标签可以从不同的域加载和执行资源(即不受同源策略影响)。
跨域问题的产生
跨域问题的产生:浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都会出现跨域的问题。
跨域问题的解决方法
Jsonp
JSONP
是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。
这里就不赘述了,有时间就整理一下
CROS
CORS(Cross-Origin Resource Sharing)跨域资源共享,是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源(允许跨域访问)。
注:整个CORS
通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS
通信与同源的AJAX
通信没有差别,代码完全一样。浏览器一旦发现AJAX
请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS
通信的关键是服务器。只要服务器实现了CORS
接口,就可以跨源通信。
CROS的请求
CORS分为两种请求,一种是简单请求,另一种是非简单请求。
为什么要分为简单请求和非简单请求?
因为浏览器对这两种请求方式的处理方式是不同的。
1. 简单请求
- 请求方式为
HEAD
、POST
或者GET
- http头信息不超出一下字段:
Accept
、Accept-Language
、Content-Language
、Last-Event-ID
、Content-Type
(限于三个值:application/x-www-form-urlencoded
、multipart/form-data
、text/plain
)
只要满足以上两个条件的请求就是简单请求;
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。
origin:"http://localhost:8080"
Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
浏览器会根据此次请求的响应头中是否包含Access-Control-Allow-Origin
来判断跨域请求是否成功;包含则成功;否则反之;
Access-Control-Allow-Origin
的值代表着允许访问该资源的"源";
此外,跨域成功的响应头中可能会包含其他字段:
如:
Access-Control-Allow-Credentials
:布尔值, 表明服务器是否允许发送Cookie;默认情况下,Cookie不包括在CORS请求之中;该字段设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。
Access-Control-Expose-Headers
:该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials
字段。
另一方面,开发者必须在Ajax
、Axios
请求中打开withCredentials
属性。
axios.defaults.withCredentials = true;
否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
注:如果要发送Cookie
,Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名。
2.非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
预检请求
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。
浏览器发现请求是一个非简单请求时,就自动发出一个"预检"请求,要求服务器确认是否可以这样请求。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
"预检"请求用的请求方法是OPTIONS
,表示这个请求是用来询问的。头信息里面,关键字段是Origin
,表示请求来自哪个源。
除了Origin
字段,"预检"请求的头信息包括两个特殊字段。
-
Access-Control-Request-Method
:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法。 -
Access-Control-Request-Headers
:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段。
预检请求的响应
服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method
和Access-Control-Request-Headers
字段以后,会有两种结果:
- 如果确认允许跨源请求,就可以做出一个包含
Access-Control-Allow-Origin
和其他字段的响应; - 如果不允许跨源请求,会做出一个没有任何CORS相关的头信息字段的响应;
服务器回应的CORS相关字段如下:
'Access-Control-Allow-Origin: '
// 跨域成功请求的响应头必须字段;
// 值为指定源 或者 *;*是指所有源
'Access-Control-Request-Method:'
// 返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
// 值为POST,GET......等
// 注:当请求是"预检"请求时,该字段是必须的
'Access-Control-Request-Headers:'
// 如果浏览器请求包括Access-Control-Request-Headers字段,
// 则Access-Control-Allow-Headers字段是必需的。
// 它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
'Access-Control-Allow-Credentials:'
// 该字段与简单请求时的含义相同。
// 布尔值, 表明服务器是否允许发送Cookie;
// 默认情况下,Cookie不包括在CORS请求之中;
// 该字段设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。
'Access-Control-Max-Age:1728000'
// 该字段可选,用来指定本次预检请求的有效期,单位为秒。
// 有效期是1728000秒,即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
使用服务器代理
服务器代理的原理大概是这样:
代理服务器和访问源(请求端)是同源的,但和被访问服务器(资源端)是不同源的,但服务器之间的访问不受浏览器同源策略的影响(即不必担心是否有跨域问题),那么我们即可请求到同源服务器上的从被访问服务器上的获取到的请求资源了;
例:例如www.123.com/index.html
需要调用www.456.com/server.php
,可以写一个接口www.123.com/server.php
,由这个接口在后端去调用www.456.com/server.php
并拿到返回值,然后再返回给index.html,这就是一个代理的模式。相当于绕过了浏览器端,自然就不存在跨域问题。
目前我所知道的两种服务器代理方法:
1. Nginx反向代理
2. Node服务器
在我所参与的Vue项目中,解决跨域问题的方式就是使用代理的方式;
开发环境下使用Node做代理;通过Webpack的配置即可实现;
生产环境下使用Nginx做代理;重写Nginx配置文件即可实现;
参考文献:
https://www.jianshu.com/p/a0dd1e712c3a
https://blog.csdn.net/java_green_hand0909/article/details/78740765
https://blog.csdn.net/lambert310/article/details/51683775
https://blog.csdn.net/qq_38128179/article/details/84956552
https://blog.csdn.net/qq_31617637/article/details/72955239