likes
comments
collection
share

浏览器跨域问题以及解决方案

作者站长头像
站长
· 阅读数 55

平时工作中,在与后端同事对接接口时,经常会遇到跨域问题。当请求产生跨域的时候,实际上是被浏览器拦截了,并没有到达服务端,所以遇到跨域别急着找后端大哥说接口出问题了~ 实际上请求还没到服务端呢。那为啥网络请求会跨域呢?这就要从浏览器的同源策略说起。

同源策略

一个 url 大概长这样子: https://www.baidu.com:443,主要由 协议域名 还有 端口号 组成,其中如果是默认端口号,那可以省略不写, http 的默认端口号是 80 ,https 的默认端口号是 443。当两个 url 以上三个信息全部相同时,我们就称这两个 url 是 同源 的。

浏览器的 同源策略 规定只有同一个源下的文档及脚本才有权限读取访问一些信息。比如:

  • cookie 、localStorage 和 IndexDB ,每个源域名下的存储信息是独立的
  • DOM 的获取
  • AJAX 请求的发送

那浏览器为什么要实行这个策略?为了 安全。如果不限制同源请求访问,那相当于一个源下的信息可以被任意的获取,这在信息安全方面毫无疑问是极大的隐患,容易被黑客利用攻击。

跨域的表现

当我们在前端发起一个 Ajax 请求时,如果源请求和目标请求 协议域名 还有 端口号 有任意一个不同,那么这个请求就违反了浏览器的同源策略,于是就产生了跨域。

请求又分为简单请求和复杂请求。简单请求 可以直接发送,它必须符合以下条件:

  • 请求方法是 GETHEAD, POST 其中之一
  • 请求头只包含 Accept Accept-LanguageContent-LanguageContent-Type
  • Content-Type 的值为 text/plainmultipart/form-dataapplication/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.comb.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 请求时,设置 withCredentialstrue
  • 检查下 samesite 属性值,确保不会限制 cookie,值为 none 则可以携带
  • 服务端要设置首部字段 Access-Control-Allow-Credentialstrue

总结

  • 为了保证信息安全,浏览器规定了同源策略
  • 请求违反了同源策略(协议、域名、端口有一个不同),就是跨域
  • 跨域的解决方案有 document.domain、JSONP 、postMessage 和 nginx 反向代理
  • 跨域时注意 cookie 的携带问题
转载自:https://juejin.cn/post/7209306358583312440
评论
请登录