报错信息
Access to XMLHttpRequest at 'https://xxx.cn/' from origin 'https://***.cn' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
当访问一个地址(https://xxx.cn/)时,被 CORS协议 阻止,没有在Header里发现 “Access-Control-Allow-Origin” 参数的资源。
跨域对比
给出了相对 https://cloud.pkpm.cn/playground/obv/ 同源检测的示例:
URL | 结果 | 原因 |
---|---|---|
https://cloud.pkpm.cn/devcenter/commonQuestion/ | 成功 | |
http://cloud.pkpm.cn/devcenter/commonQuestion/ | 失败 | 不同协议 ( https和http ) |
https://cloud.pkpm.cn:8081/playground/obv/ | 失败 | 不同端口号 ( 8081和8080) |
https://api.cloud.pkpm.cn/ | 失败 | 不同域名 ( cloud.pkpm 和 api.cloud.pkpm) |
同源策略
同源即 协议,域名,端口号 三个完全一致,才能称作同源,是浏览器的一个安全限制,从一个源加载的文档或者脚本默认不能访问另一个源的资源。
例如:a.com/111/html页面不能访问b.com/person这种接口,因为他们是不用的源。
注意:下面几个不受同源策略限制
1.页面中链接(例如页面的 <a href="https://www.baidu.com/">百度</a>
)、重定向和表单提交不受同源策略限制。
2.跨域资源的引入是不受同源策略的限制,但是js读不到其中的内容<script src="..."></script>,<img>,<link>,<iframe>等
。
跨域的目的
Ajax 的同源策略主要是为了防止 CSRF(跨站请求伪造) 攻击,如果没有 AJAX 同源策略,相当危险,我们发起的每一次 HTTP 请求都会带上请求地址对应的 cookie,那么可以做如下攻击:
- 用户登录了自己的银行页面 http://mybank.com,http://mybank.com向用户的cookie中添加用户标识。
- 用户浏览了恶意页面 http://evil.com。执行了页面中的恶意AJAX请求代码。
- http://evil.com向http://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。
- 银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
- 而且由于Ajax在后台执行,用户无法感知这一过程。
DOM同源策略也一样,如果 iframe 之间可以跨域访问,可以这样攻击:
- 做一个假网站,里面用iframe嵌套一个银行网站 http://mybank.com。
- 把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
- 这时如果用户输入账号密码,我们的主网站可以跨域访问到http://mybank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。
所以有了跨域访问限制之后,我们才能够安全的上网。
名词解释
name | value | 含义 |
---|---|---|
access-control-allow-credentials | true | 是否允许后续请求携带认证信息(cookies),该值只能是true,否则不返回 |
access-control-allow-headers | x-requested-with,content-type | 允许的请求头字段,允许请求中携带字段 |
access-control-allow-methods | GET, POST, PUT, PATCH, DELETE, PURGE, OPTIONS | 允许的请求类型 |
access-control-allow-origin | Accept-Ranges,Content-Encoding,Content-Type,Content-Length,Content-Range,etag | 指定允许其他域名访问,标识允许哪个域的请求 |
access-control-expose-headers | Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma | 跨域访问的响应头 |
access-control-max-age | 86400 | 在某个范围内,不发送预检请求 |
access-control-request-method | 本次请求的请求方式 | |
access-control-request-headers | 本次请求的自定义请求头字段 |
表格中的 Access-Control-Allow-Origin
有多种设置方法:
- 设置
*
是最简单粗暴的,但是服务器出于安全考虑,肯定不会这么干,而且,如果是*
的话,浏览器将不会发送cookies,即使你的XHR设置了withCredentials。 - 指定域,如https://198.162.0.129,一般的系统中间都有一个nginx,所以推荐这种。
- 动态设置为请求域,多人协作时,多个前端对接一个后台,很方便。
这里有个注意点:
Access-Control-Request-Method
,Access-Control-Request-Headers
返回的是满足服务器要求的所有请求方式和请求头,不只是当前次的请求
解决方案
(1)跨域资源共享(CORS)
定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问
//指定允许其他域名访问
'Access-Control-Allow-Origin:*'//或指定域
//响应类型
'Access-Control-Allow-Methods:GET,POST'
//响应头设置
'Access-Control-Allow-Headers:x-requested-with,content-type'
(2)通过jsonp跨域
通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。
优点:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。
缺点:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
<script type="text/javascript">
function doSomething(jsondata){
//处理获得的json数据
}
</script>
<script src="https://example.com/a.html?callback=doSomething"></script>