浏览器跨域问题以及解决方案
平时工作中,在与后端同事对接接口时,经常会遇到跨域问题。当请求产生跨域的时候,实际上是被浏览器拦截了,并没有到达服务端,所以遇到跨域别急着找后端大哥说接口出问题了~ 实际上请求还没到服务端呢。那为啥网络请求会跨域呢?这就要从浏览器的同源策略说起。
同源策略
一个 url 大概长这样子: https://www.baidu.com:443
,主要由 协议
,域名
还有 端口号
组成,其中如果是默认端口号,那可以省略不写, http 的默认端口号是 80
,https 的默认端口号是 443
。当两个 url 以上三个信息全部相同时,我们就称这两个 url 是 同源
的。
浏览器的 同源策略
规定只有同一个源下的文档及脚本才有权限读取访问一些信息。比如:
- cookie 、localStorage 和 IndexDB ,每个源域名下的存储信息是独立的
- DOM 的获取
- AJAX 请求的发送
那浏览器为什么要实行这个策略?为了 安全
。如果不限制同源请求访问,那相当于一个源下的信息可以被任意的获取,这在信息安全方面毫无疑问是极大的隐患,容易被黑客利用攻击。
跨域的表现
当我们在前端发起一个 Ajax 请求时,如果源请求和目标请求 协议
,域名
还有 端口号
有任意一个不同,那么这个请求就违反了浏览器的同源策略,于是就产生了跨域。
请求又分为简单请求和复杂请求。简单请求
可以直接发送,它必须符合以下条件:
- 请求方法是
GET
,HEAD
,POST
其中之一 - 请求头只包含
Accept
,Accept-Language
,Content-Language
,Content-Type
Content-Type
的值为text/plain
,multipart/form-data
,application/x-www-form-urlencoded
其中之一
不符合以上条件的请求就是复杂请求
,复杂请求在发送之前,浏览器需要先发一个预检请求,请求方法为 OPTIONS,询问服务器是否支持跨域,内容大概是这样:
OPTIONS /resource/foo
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type, x-requested-with
Origin: https://foo.bar.org
服务器会在响应 header 的 Access-Control-Allow-*
系列字段里包含它支持的源信息,大概是下面这样:
HTTP/1.1 200 OK
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Origin: https://foo.bar.org
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Allow-Headers: Content-Type, x-requested-with
Access-Control-Max-Age: 86400
浏览器判断当前请求是否符合,如果不符合,则跨域报错信息如下:
Access to XMLHttpRequest at '×××' from origin '×××' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
遇到这样的错误提示,建议找后端同学,先检查下服务端有没配置好 Access-Control-Allow-Origin
这个字段,特别注意环境区分,有时特定环境下忘记配了也是常有的事。通常你的本机 ip : http://localhost:8080
要添加进允许源里去。
跨域的解决方案
document.domain = '×××'
适用于站点和服务器享有相同的基本域名,比如a.example.com
和b.example.com
这时可以设置 document.domain = 'example.com' 来规避跨域CORS
跨域资源共享,有了这个机制,浏览器可以向跨域服务器发出请求,只是要先预检校验。JSONP
利用script
标签没有跨域限制的特性,通过 src 属性指向要访问的地址并提供一个回调函数来接收数据
// JSONP 的简单实现
<script type="text/javascript">
var handleData = function (data) {
console.log(data)
}
var url = 'https://example.com?callback=handleData'
var script = document.createElement('script')
script.setAttribute('src', url)
document.getElementsByTagName('head')[0].appendChild(script)
</script>
postMessage
window.postMessage 这个 API 可以跟另一个页面通信,传递信息,而不会有跨域问题- 使用
nginx 反向代理
通常是服务端同学去操作
另外,在跨域时还会遇到 cookie 的携带问题,以下是解决思路:
- 必须在初始化 Ajax 请求时,设置
withCredentials
为true
- 检查下 samesite 属性值,确保不会限制 cookie,值为
none
则可以携带 - 服务端要设置首部字段
Access-Control-Allow-Credentials
为true
总结
- 为了保证信息安全,浏览器规定了同源策略
- 请求违反了同源策略(协议、域名、端口有一个不同),就是跨域
- 跨域的解决方案有 document.domain、JSONP 、postMessage 和 nginx 反向代理
- 跨域时注意 cookie 的携带问题
转载自:https://juejin.cn/post/7209306358583312440