Skip to content

跨域

协议 + 域名 + 端⼝三者相同才为同源

前后端的跨域

Jsonp

js
// 前端
const script = document.createElement('script'); 
script.type = 'text/javascript'; 
// 接口?数据&回调函数
script.src = 'url?user=admin&callback=cb';
document.appendChild(script);
function cb(res){
    JSON.parse(res);
}
    
// 后端,发送一个传入了数据的回调执行函数
cb(JSON.stringify({code: 1});

nginx 跨域

shell
# 简单例子
http {
    # 监听 localhost:9000 ,"/" 则代理到 localhost:3000,"/api" 则代理到 localhost:8080
    server {
        listen       9000;
        server_name  localhost;

        location / {
            proxy_pass http://localhost:3000;
        }

        location /api/ {
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host;
        }
    }
}

cors

  1. GETPOSTHEAD请求
    • 响应头中有一个 Access-Control-Allow-Origin 字段,用来记录可以访问该资源的域。
    • 当浏览器收到这样的响应头信息之后,提取出 Access-Control-Allow-Origin 字段中的值,发现该值包含当前页面所在的域,就知道这个跨域是被允许的,因此就不再对前端的跨域请求进行限制
  2. 其他请求
    • 客户端发送 OPTIONS 询问:Access-Control-Request-Method DELETE
    • 服务器端响应
    • 在发送跨域请求

WebSoocket

本身就没有同源限制

js
const ws = new WebSocket("wss://echo.websocket.org");

dev-server 代理跨域(用于开发时)

vue-cli:在项目根目录的 vue.config.js 中添加以下代码,开启 proxy,具体配置看查看 options

js
module.exports = {
    devServer: {
        proxy: {
            // 代理跨域
            '/api': {
                target: 'http://localhost:8080', // 后端端口修改
                secure: false
            }
        }, // 配置多个代理
    }
};

create-react-app:只能代理一个(/api),可以在 package.json 中加入

如果 react 需要代理多个,可以使用库 http-proxy-middleware

json
{
  "proxy": "http://localhost:8080"
}

vite:在 vite.config.js 中添加 proxy 规则

js
export default {
  server: {
    proxy: {
      // 字符串简写写法
      '/foo': 'http://localhost:4567/foo',
      // 选项写法
      '/api': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      },
      // 正则表达式写法
      '^/fallback/.*': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/fallback/, '')
      }
    }
  }
}

可以直接模拟 cors 跨域

js
export default {
  server: {
    cors: true
  }
}

前端间的跨域

domain + iframe

在主域相同,⼦域不同的跨域,可进行跨域

  • 父窗口
html
<iframe id="iframe" src="http://www.domain.com/b.html"></iframe>
<script>
	document.domain = "domain.com";
    // 通过 bWindow 获取数据了
    const bWindow = document.getElementById("iframe").contentWindow;
    const data = bWindow.data;
    // 通过 bDocument 给 b 窗口数据
    const bDocument = document.getElementById("iframe").contentDocument;
    bDocument.getElementById('p').innerText = "from A To B";
</script>
  • 子窗口
html
<p id="p" style="display:none"></p>
<script>
	document.domain = "domain.com";
    // 传数据给 A 窗口,要和 A 协商好 key
    window.data = "from B to A"
    // 得到 A 给 B 的数据
    const data2 = document.getElementById('p').innerText;
</script>

XDM

  • 不会发送给服务端,是客户端和客户端之间的通讯
  • 发送方使用 postMessage() 发送消息给接收方
    1. 消息的字符串
    2. 接受源的字符串
js
window.postMessage('hello', 'https://www.baidu.com')
  • 任何窗口都可以监听 message 事件,接受可能会传过来的信息。event 对象上有以下属性
    1. data:消息
    2. origin:发送消息的源
    3. source:发送源的 window 的代理,其上只有一个可用的方法——postMessage
js
window.addEventListener('message', event => {
    if (event.origin === 'http://127.0.0.1') {
		event.source.postMessage('received', 'http://127.0.0.1')
    }
})

因为 message 事件可以接受所有域的消息,所以必须验证发送域是否是可信的域,才能使用其消息