跨域

跨域

如果协议、域名或者端口有一个不同就是跨域。

 

同源策略

推荐阅读:浏览器同源政策及其规避方法

因为浏览器出于安全考虑(主要用来 防止信息泄露 和 CSRF 攻击),制定了同源策略,意思就是跨域时 Ajax 请求会失败。

问题:

  然而同源策略还是不能完全阻止 CSRF,它只是把 Response 给拦截了,就是请求还是发出去了。

  换句话说只能阻止获取 Response 的信息,不能阻止发送请求

例子:

  有个恶意网站,一打开就会跨域发送这样一个请求,ajax 直接发出去 https://xxx.com/trade?money=1000&userId=666 (xhr.withCredentials = true 时还会带上cookie)

  虽然 xhr 的 response 恶意网站收不到,但是这个请求确确实实发出去了

结论:

  因此,真正的安全应该在服务端做好。例如,cookie 和 ip 绑定、检查 reference、用 token 来替代 cookie,这些都是解决csrf 的方法。

 

跨域请求

因为同源策略的存在,我们不得不通过一些手段,去实现跨域请求。

我知道的有以下8种:JSONP、CORS、WebSocket、postMessage、hashchange、window.name、代理、document.domain

JSONP

  利用 script、img 的 src 不受同源策略限制的特点,发送跨域的 GET 请求。

  实现这个需要前后端都改写

  例如:

<script src="http://domain/api?param1=a&param2=b&callback=xxx"></script>
<script>
function xxx(data) {
    console.log(data)
}
</script>
GET  http://domain/api?param1=a&param2=b&callback=xxx 会返回这样的
xxx({
    errCode: 0,
    data: {
        list: [{age: 1}, {age: 2}, {age: 3}, {age: 4}, {age: 5}],
        page: 1,
        size: 5,
        total: 100
    }
})

上面定义的全局函数xxx,就可以接受到服务器返回的 data 了

 

CORS

Cross-origin resource sharing 跨域资源分享

这个十分简单,只需要后端改写一下,加几个 Access-Control-Allow-xxx 的响应头就可以了。前端完全不用修改,跨域请求当作正常请求来发送就可以了。

很早之前就写过博文了,这里就不细说了:Ajax跨域CORS

 

WebSocket

普通版 ws:// 和 加密版 wss://,这个协议不受同源策略限制,因此可以跨域。

 

postMessage

通过 window.open 或者 iframe.contentWindow 拿到的 window 对象可以用 postMessage 来通讯,这个不需要 document.domain 一致,都可以通讯。

这个纯前端就能做到,推荐阅读:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

// 发送方
let otherWin = window.open('https://www.qq.com');
otherWin.postMessage('信息xxx', '*');

// 接收方
window.addEventListener('message', ev => {
    console.log(ev.data);
})

 

hashChange

H5之前,和 iframe 通讯可以用 hashChange 事件来实现跨域通讯

// 设置iframe的hash
var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;

// iframe 监听 hashchange 事件
window.onhashchange = function () {
    var data = window.location.hash;
};

 

window.name

window.name有个特性,就是可以继承上一个页面的window.name

利用这个特性 + iframe,可以跨域通讯。

iframe 访问异域网站,异域网站脚本设置 window.name

iframe src 设置为同域页面,这个页面就继承了之前设置的 window.name

这时候iframe和主页面,就不存在跨域了,可以正大光明地拿到 iframe.contentWindow.name

 

代理

起一个代理服务器,例如配置一个 Nginx ,如果需要跨域的请求,就发送成特定的域内请求,靠后台识别,代理到指定的服务器。最常用的就是 webpack-dev-server 的 proxy(就是 express 的 http-proxy-middleware)。

 

document.domain

document.domain 如果能设置成一致的话,2个页面就可以通过 iframe 或者 window.open,互相操作dom。

当然 document.domain 只能设置成自己或者比自己更高级的域名,例如 xxx.qq.com 只能设置成 xxx.qq.com 或者 qq.com

这个纯前端就能做到, 但是这个不是传统意义的跨域通讯,而是跨域修改dom

// iframe 
// 拿到 iframe 的 window
document.querySelector('iframe').contentWindow
// iframe 也能拿到 宿主的window
window.parent

// window.open 返回值就是打开页面的 window
let openedWin = window.open('https//www.cnblogs.com');
// 被打开的可以通过 window.opener 获取打开者
window.opener

但是如果 document.domain 不一致,就会报跨域的错。

 ps:www.baidu.com 和 baidu.com 是跨域吗?

是的,但是如果document.domain都设置为baidu.com,那就可以跨域互相请求了。

 

posted @ 2019-08-25 23:37  张啊咩  阅读(216)  评论(0编辑  收藏  举报