amandakelake/blog

前端主流跨域方法

amandakelake opened this issue · 0 comments

跨域的本质

跨域是浏览器的限制,服务器之间的请求是没有跨域限制的
所以本地node起的服务器或者nginx服务器有两个作用
1、充当静态文件服务器,可以查看本地页面,以及监测文件改动
2、充当代理服务器,比如node的proxyTable用的是http-proxy-middleware中间件,原理是浏览器发给自己的服务端,然后由自己的服务端再转发给要跨域的服务端,做一层代理

在浏览器端跨域,可能导致获取到其他网站的敏感信息或者越权操作,比如拿到银行的登录状态或者执行转账操作,所以应当禁止。
服务端跨域没有这个问题,因为用户的这些状态信息都是在浏览器端保存的,服务器只能有自己网站的状态信息

到目前为止,常见的跨域方法有以下几种

  • CORS
  • postMessage
  • 各种插件:比如http-proxy-middleware
  • Websocket

还有这些老生常谈的,但不经常用的,就忽略而过吧

  • document.domain
  • location.hash
  • window.name
  • JSONP

CORS 跨域资源共享

只需要后端同学支持就ok,前端不需要做很多额外工作(除了携带cookie)。
只要服务器返回的相应中包含头部信息Access-Control-Allow-Origin: domain-name,domain-name为允许跨域的域名,也可以设置成*,浏览器就会允许本次跨域请求

后端允许CROS跨域,前端设置代理链接和允许带上cookie

后端header设置

Access-Control-Allow-Origin不可以为 *,因为 *会和 Access-Control-Allow-Credentials:true 冲突,需配置指定的地址

access-control-allow-credentials: true
access-control-allow-origin: http://localhost:9123
前端设置,以vue+axios举个例子
// 此处是允许带上cookie
axios.defaults.withCredentials = true;

代理的话,现在前后端分离的潮流,都是node服务器起的代理proxyTable

proxy: {
  "/fd": {
    target:
      process.env.NODE_ENV === "production"
        ? "http://m.domian1.com"
        : "http://test.domain.com",
    ws: true,
    changeOrigin: true,
    pathRewrite: {
      "/fd": "/"
    }
  }
},

postMessage

otherWindow.postMessage(message, targetOrigin, [transfer]);
MDN-postMessage

  • otherWindow: 其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames
  • message:消息内容
  • targetOrigin: 接受消息窗口的源,即”协议 + 域名 + 端口”
  • transfer:是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

发送者和接收者都可以通过message事件,监听对方的消息
message事件的事件对象event包含三个属性

  • event.source: 发送消息的窗口对象的引用,可以用此在两个窗口建立双向通信。
  • event.origin: 发送消息的URI
  • event.data: 消息内容

发送方 http://domain-a.com/a.html

<script>
  var newWindow = window.open('http://domain-b.com/b.html');
  /* 向b.html发送消息 */
  newWindow.postMessage('Hello', 'http://domain-b.com/b.html');
  /* 双向通信,接收b.html的回复消息 */
  var onmessage = function (event) {
    var data = event.data;
    var origin = event.origin;
    var source = event.source;
    if (origin == 'http://domain-b.com/b.html') {
      console.log(data); //Nice to see you!
    }
  };
  window.addEventListener('message', onmessage, false);
</scirpt>

接收方 http://domain-b.com/b.html

<script>
  var onmessage = function (event) {
    var data = event.data;
    var origin = event.origin;
    var source = event.source;
    if (origin == 'http://domain-a.com/a.html') {
      console.log(data); //Hello
      /* 回复a.html的消息 */
      source.postMessage('Nice to see you!', 'http://domain-a.com/a.html');
    }
  };
  window.addEventListener('message', onmessage, false);
</script> 

WebSocket

/* websocket协议为ws/wss, 类似http/https的区别 */
wsUrl = 'wss://127.0.0.1:8090/ws/';
/* 发送 */
ws = new WebSocket(wsUrl);
/* 连接成功建立时调用 */
ws.onopen = function (event) {
  console.log("websocket command onopen");
  var msg = {
    username: 'YeaseonZhang'
  }
  /* 通过 send() 方法向服务端发送消息,参数必须为字符串 */
  ws.send(JSON.stringify(msg));
};
/* 服务端向客户端发送消息时调用 */
ws.onmessage = function (event) {
  /* event.data包含了服务端发送过来的消息 */
  console.log("websocket command onmessage: " + event.data);
  if (event.data === 'success') {
    /* 通过 close() 方法断开websocket连接 */
    ws.close();
  }
};
/* 连接被关闭时调用 */
ws.onclose = function (event) {
  console.log("websocket command onclose: " + event.data);
};
/* 出现错误时调用 */
ws.onerror = function (event) {
  console.log("websocket command onerror: " + event.data);
};

插件:比如http-proxy-middleware

其实就是我们日常前后端分离中,node起的最多服务器设置,但一些脚手架,比如vue cli ,create-react-app都帮我们配置好了

var express = require('express');
var proxy = require('http-proxy-middleware');

var app = express();

app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));
app.listen(3000);