为什么XMLHTTPRequest不能跨域请求资源

12,351 阅读4分钟

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

什么是 XMLHttpRequest?

XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。

XMLHttpRequest 在 AJAX 编程中被大量使用。

什么是跨域?

跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 JavaScript 施加的安全限制。

什么是同源策略?

同源策略是一个重要的安全策略,它用于限制一个 origin 的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。

下表给出了与 URL https://juejin.cn 的源进行对比的示例:

URL结果原因
https://juejin.cn/user_api/v1/user/profile_id同源只有路径不同
http://juejin.cn/interact_api跨域协议不同
https://juejin.cn:8888/list_api跨域端口不同 ( http:// 默认端口是80)
https://mcs.snssdk.com/v1/list跨域主机不同

为什么不让跨域?

同源策略的目的是为了保证网络安全,防止数据被恶意窃取。

以下三种行为会受到限制:

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM 无法获得
  • AJAX 请求不能发送

如何解决跨域问题?

CORS

Cross-origin resource sharing (跨域资源共享) 简称 CORS。

一般是从请求中读取 Origin 标头并包含一个响应标头,说明请求源是被允许的。例如,考虑一个接收以下请求的应用程序:

GET /sensitive-victim-data HTTP/1.1
Host: vulnerable-website.com
Origin: https://malicious-website.com
Cookie: sessionid=...

然后它响应:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true
...

这些标头声明允许来自请求域 ( malicious-website.com) 的访问,并且跨域请求可以包含 cookie ( Access-Control-Allow-Credentials: true),因此将在会话中进行处理。

Nginx 代理

nginx 配置解决 iconfont 跨域

浏览器跨域访问 js、css、img 等常规静态资源被同源策略许可,但 iconfont 字体文件 (eot|otf|ttf|woff|svg) 例外,此时可在 nginx 的静态资源服务器中加入以下配置。

location / {
  add_header Access-Control-Allow-Origin *;
}

nginx 反向代理接口跨域

可以跨域的原理:同源策略是浏览器的安全策略,不是 HTTP 协议的一部分。服务器端调用 HTTP 接口只是使用 HTTP 协议,不会执行 JS 脚本,不需要同源策略,也就不存在跨越问题。

实现思路:通过 nginx 配置一个代理服务器(域名与 domain1 相同,端口不同)做跳板机,反向代理访问 domain2 接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域登录。

nginx 具体配置:

#proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

Nodejs 代理

利用服务端之间请求不会跨域的特性

  • webpack 配置 proxy
  • Vue-cli 中配置 proxy

WebSocket

WebSocket protocol 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是 server push 技术的一种很好的实现。

// 初始化一个 WebSocket 对象
var ws = new WebSocket("ws://localhost:9999/echo");

// 建立 web socket 连接成功触发事件
ws.onopen = function () {
  // 使用 send() 方法发送数据
  ws.send("发送数据");
  alert("数据发送中...");
};

// 接收服务端数据时触发事件
ws.onmessage = function (evt) {
  var received_msg = evt.data;
  alert("数据已接收...");
};

// 断开 web socket 连接成功触发事件
ws.onclose = function () {
  alert("连接已关闭...");
};

总结

  • 域名协议端口不同时会导致跨域现象
  • 常见解决跨域问题的方法要牢记在心
  • 本文介绍的内容都比较粗略,欢迎在评论区补充细节