跨域
如果协议、域名或者端口有一个不同就是跨域。
同源策略
推荐阅读:浏览器同源政策及其规避方法
因为浏览器出于安全考虑(主要用来 防止信息泄露 和 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¶m2=b&callback=xxx"></script> <script> function xxx(data) { console.log(data) } </script>
GET http://domain/api?param1=a¶m2=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,那就可以跨域互相请求了。