跨域

180 阅读3分钟

什么是跨域

浏览器执行同源策略, 即协议、域名、端口号三者必须全部一致,限制的行为如cookie、localStorage、Dom元素、ajax请求等

跨域的常见解决办法

jsonp

  • jsonp的实现原理
    它是利用script标签的src属性来实现跨域.它将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后再返回,实现服务器端向客户端通信。
  • 简单实现
// 创建一个script标签,将传递的参数拼接赋值给script的src
function jsonp({ url, params, cb }) {
    return new Promise((resolve, reject) => {
        // 声明全局函数
        window[cb] = function (data) {
            resolve(data);
            document.body.removeChild(script);
        };
        params = {...params, cb};
        let arrs = [];
        for (let key in params) { // 将参数拼接成wd=love&cb=show
            arrs.push(`${key}=${params[key]}`);
        }
        let script = document.createElement('script');
        script.src = `${url}?${arrs.join('&')}`;
        document.body.appendChild(script);
    })
}

jsonp({
    url: 'http://localhost:3000/say',
    params: {
        wd: 'love',
    },
    cb: 'show',
}).then(data => {
    console.log(data);
});

缺点: 只能发送get请求

跨域资源共享CORS

CORS允许浏览器向跨源服务器发送ajax请求,它需要浏览器和服务器同时支持。当浏览器发现AJAX请求跨源,会自动添加一些附加的头信息,服务器实现对应的CORS接口,有时还会多出一次附加的请求,然后可以跨源通信。

  • 相关CORS
Access-Control-Allow-Origin // 允许访问的源
Access-Control-Allow-Headers // 允许请求头
Access-Control-Allow-Credentials // 允许携带cookie, 此时,客户端也需要在在AJAX请求中打开withCredentials属性 CORS请求默认不发送Cookie和HTTP认证信息
Access-Control-Expose-Headers  // 允许前端获取头 预检请求时使用
Access-Control-Allow-Methods // 允许方法 预检请求时使用
Access-Control-Max-Age // 预检间隔时间
...

postMessage

H5提供的跨域请求方法.

// 发送信息
<iframe src="http://localhost:3009/b.html" frameborder="0" id='frame' onload="load()"></iframe>
<script>
    // 页面间通信
    function load () {
        let frame = document.getElementById('frame');
        // 拿到b窗口的window 向其发送信息
        frame.contentWindow.postMessage('我是a', 'http://localhost:3009');
        window.onmessage = (e) => {
            console.log(e.data);
        }
    }
</script>

// 接收信息
<script>
    window.onmessage = (e) => {
        console.log(e.data);
        e.source.postMessage('我是b', e.origin);
    }
</script>

window.name

window.name属性可以在不同的域下保存,通过子窗口之间通信实现数据传输.跨域实现如下: a和b是同域--> http://localhost:3000
c--> http://localhost:3009
目标: a获取c的数据
实现过程: 在a.html中通过iframe引用c,c把值放在window.name,a把引用的地址改到b,通过b拿到c数据

// a.html
<iframe src="http://localhost:3009/c.html" frameborder="0" id='frame' onload="load()"></iframe>
<script>
    let first = true;
    function load() {
        if (first) {
            let iframe = document.getElementById('iframe');
            iframe.src="http://localhost:3000/b.html";
            first = false;
        } else {
            console.log(iframe.contentWindow.name);
        }
        
    }
</script>

// c.html
<script>
    window.name = '我是c';    
</script>

页面中的iframe支持跨域,iframe-b可以拿到iframe-c中的数据.a和iframe-b同源,所以a可以通过b拿到c的数据

location.hash

a和b是同域--> http://localhost:3000
c--> http://localhost:3009
目标: a获取c的数据
实现过程: a给c传递hash值 c将此hash传递给b b将结果放到a的hash中

// a.html
<iframe src="http://localhost:3009/c.html#iama" frameborder="0" id='frame'></iframe>
<script>
    window.onhashchange = () => {
        console.log(location.hash, 'c传递的hash');
    }
</script>

// b.html
<script>
    window.parent.parent.location.hash = location.hash;    
</script>

// c.html
<script>
    console.log(location.hash, 'a传递的hash');
    let iframe = document.createElement('iframe');
    iframe.src = 'http://localhost:3000/b.html#iamc';
    document.body.appendChild(iframe);
</script>

websocket

此处使用第三方插件websocket.io

// 客户端
let socket = new WebSocket('ws://localhost:3000');
socket.onopen = () => { // 连接成功
    socket.send('我是a'); 
}
socket.onmessage = (e) => { // 监听服务端消息
    console.log(e.data);
}

// 服务端
let express = require('express');
let app = express();
let WebSocket = require('ws');
let wss = new WebSocket.Server({port: 3000}); 
wss.on('connection', (ws) => { // 连接成功
    ws.on('message', (data) => {
        console.log(data);
        
    })
    ws.send('我是服务');
})

nginx

location / { #访问文件路径
    try_files $uri /index.html;
}
location ~* .*(html|json)$ {
    expires -1s;
    add_header 'Cache-Control' 'no-store';
    add_header 'Access-Control-Allow-Origin' '*'; # 添加跨域头
}

domain

一般针对二级域名,结合iframe实现跨域