一、什么是浏览器的同源策略
schemehttp、httpshost域名、ipport端口号
如果scheme://host:port都相同,那么就是同源,否则是跨域CORS
跨域请求一般指的是AJAX异步请求,比如XHR和fetch,其他以此为底层请求的都属于此类(比如axios)。
src加载的img资源不属于AJAX,因此不受同源策略限制,JSONP库就是利用这个特点封装请求。
不经过浏览器发起AJAX请求不受CORS的限制,因为CORS是浏览器的限制。
二、跨域的访问
第一种:满足简单跨域,浏览器允许发出请求
条件 1.请求方法属于下面的一种(简单的两种请求方式):
- 。get
- 。post|
- 。head
条件 2. 请求头仅包含安全的字段,常见的安全字段如下(也就是不自己添加自定义请求头):
- 。Accept
- 。Accept-Language
- 。Content-Language
- 。Content Type
- 。DPR
- 。Downlink
- 。Save-Data
- 。Viewport- width
- 。Width
条件 3. 请求头如果包含Content-Type,仅限下面的值之一(也就是简单的form表单请求):
- 。text/plain
- 。multipart/form-data
- 。application/x-www form- urlencoded
对于简单跨域请求,浏览器端运行放行,这个请求得以发给服务器端
服务器端根据自己的CORS策略,设置响应头:Access-Control-Allow-Origin: *,表示同意所有跨域请求获取资源。
浏览器收到服务器端的同意跨域后,才接收当前请求的响应数据。
总结:对于简单跨域,浏览器端不用设置请求头,服务器端需要设置Access-Control-Allow-Origin: *或指定Origin
从上面就可以看出来,CORS就是浏览器为了自己安全着想搞出来的蛾子;
首先,对于不符合CORS跨域的请求,直接不给发出去,根本没服务器端的事儿;
对于符合的跨域,发出请求,让服务器决定是否可以跨域,如果服务器不同意,浏览器也会禁止自己拿数据。
一般遇到的错误:CORS header 'Access-Control-Allow-Origin' missing
解决办法:在服务器端添加Access-Control-Allow-Origin
const allowOrigin = ["http://localhost:9000"];
let origin = req.headers["origin"];
if (allowOrigin.includes(origin)) {
resp.setHeader("Access-Control-Allow-Origin", origin);
}
第二种:需要预检的跨域请求
此类请求稍微更加敏感了,因为:
比如:请求头的Content-Type改为了application/json,等等其他超越简单请求的改动。
这类请求,浏览器端不会(不敢)直接发送请求体的数据,而是先发送一个预检请求(method=Options)
然后服务器端返回响应头信息,浏览器会检查响应头中的:请求改动的东西是否被允许
比如浏览器AJAX设置了Content-Type改为了application/json,那么浏览器就会检查响应头中有无:Access-Control-Allow-Headers: Content-Type
说白了,还是浏览器自己检查自己,
报错参考:developer.mozilla.org/zh-CN/docs/…
解决办法:在服务器端添加:
resp.setHeader("Access-Control-Allow-Origin", "具体的跨域源origin");
resp.setHeader("'Access-Control-Allow-Method", "*");
resp.setHeader("Access-Control-Allow-Headers", "*");
直接放通所有,只做跨域的源检查即可。
CORS只是限制浏览器端跨域获取资源,并不能防止非浏览器获取服务器资源
禁止非法访问资源应该使用服务器端密码校验等其他手段。
第三种:携带cookie等, 的跨域访问(withCredentials: true)
AJAX请求如果设置了withCredentials: true,那么
服务器端,需要在响应头中添加:Access-Control-Allow-Credentials: true
要特别注意的是:对于附带身份凭证的请求,服务器不得设置Access -Control-Allow-Origin的值为*
解决办法:在服务器端添加:
resp.setHeader("Access-Control-Allow-Origin", "具体的跨域源origin");
resp.setHeader("Access-Control-Allow-Method", "*");
resp.setHeader("Access-Control-Allow-Headers", "遍历前端传过来的,,,");
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setHeader("Access-Control-Expose-Headers", "Authorization,Abc,具体");
注意:Access-Control-Expose-Headers是
告诉浏览器要不要把除了基本的header之外的其他自定义header暴露给前端AJAX代码获取,
比如服务器想给前端传Header:
resp.setHeader("Authorization", "123");
一般网站会使用
header(比如header-token:KJDDKND...)和cookie同时做身份校验所以服务器端只要指定允许的跨域请求的源即可,其他放开
二、跨域的解决方案
- 1、反向代理:把前端、后端同时代理到同一个域名下
- 2、JSONP,利用src请求资源不受同源策略影响的漏洞(未测试过)
- 3、服务器设置允许跨域请求(如果要携带cookie,前端还要设置
withCredentials: true)