跨域问题总结

428 阅读3分钟

前言

这几天的工作中,碰到了一个完全不同域之间的跨域问题,发现自己竟然无法确定能不能实现跨域,可见基础知识完全被抛之脑后了,趁周末整理一下。

为什么会存在跨域

跨域是因为浏览器有同源策略,同源策略的定义是协议、域名、端口都要一致。 在跨域的情况下,浏览器出于安全考虑,会限制不同域之间的数据或DOM交互行为,如:

  1. Cookie、LocalStorage 和 IndexDB 无法读取。

  2. DOM 无法获得。

  3. AJAX 请求不能发送。

关于同源策略的具体概念请参考MDN

为什么需要同源策略呢。那我们试想一下如果浏览器没有同源策略的情况。

打个最基本的比方,现在你新建了一个站点:

假如没有跨域限制,那是不是可以随便读取天猫的cookie,localStorage, indexDb 等信息,如果这些信息里有用户名和密码或其他敏感信息,刚好又没有加密的话,那岂不是都能拿到,这怎么可能。

假如没有跨域限制,你把iframe的src指向天猫,让用户去登陆,然后你就可以获取iframe里面的dom元素,那岂不是账号密码唾手可得。可以访问dom的话,只要iframe引入其他公司的站点,想怎么改就怎么改,那好多公司都要倒闭了。

假如没有跨域限制,就可以向天猫等站点发请求,那岂不是可以随便提交别人的订单。这明显不合理啊。世界会乱。

但跨域问题总要解决的,因为有时候业务中确实需要跨域

下面列出八种跨域解决方案

jsonp

原理:使用script标签,src的值为后端某个文件,加上callback参数。返回一段js字符串。

<!-- fn 需自定义 -->
<script src="http:xx/xx.php?callback=fn"></script>

缺点:只能用于 get 请求。如果需要 post,可以试试用 form 提交来 hack。

document.domain

原理:通过设置document.domain来跨域。

缺点:只适用于主域相同,子域不同的业务场景。

hash

原理:利用hash改变,页面不会刷新的特点。父窗口在iframe的src上增加hash值,并在子窗口监听 hashChange事件。

缺点:只能通过 hash 传递有效信息, 只能设置字符串类型。

window.name

原理:在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个 window.name

通常做法是用一个隐藏的iframe 去加载一个同域的代理文件:proxy.html,在代理文件中设置window.name,再动态替换iframe的src为外域的url,这样就可以通过window.name传递有效信息。

因为proxy.html 和 真实需要跳转的外域 url 都是在同一个iframe下,window对象是同一个。

如果需要回传数据给父窗口,就反着来。可以先用iframe打开外域的url,在外域的文件中设置 window.name后,再跳转到 同域的 proxy.html。父窗口这时再获取

缺点:需要一个代理文件 proxy.html。window.name 只能设置字符串类型。

postMessage

原理:说不清, 此乃浏览器自带 API。具体用法就不介绍了。

缺点:接收方需要监听 message 事件。

webSocket

原理:webScoket采用的是 ws:// 或 wss:// 协议,非 http 协议。协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信,需要在服务端设置白名单。

缺点:说不清

CORS

原理:说不清

缺点:对于复杂请求,如非get,或数据格式为json的请求, 会触发两次请求。一次为 OPTIONS 预检请求,第二次才是正式请求。

服务器代理跨域

原理:了解一点 — 利用后端访问接口不存在跨域的特点。

缺点:说不清

日常前/后端配合开发中,个人觉得配置好CORS即可。