一、跨域是什么?
同源策略
跨域问题其实就是浏览器的同源策略所导致的。
什么是同源
protocol(协议)、domain(域名)、port(端口)三者一致。除此之外,都是跨域。
二、如何解决跨域?
- CORS
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器「不同的域、协议或端口」请求一个资源时,资源会发起一个「跨域 HTTP 请求」。
服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
//指定允许其他域名访问
'Access-Control-Allow-Origin:*'//或指定域
//响应类型
'Access-Control-Allow-Methods:GET,POST'
//响应头设置
'Access-Control-Allow-Headers:x-requested-with,content-type'
想要传递 cookie 需要满足 3 个条件:
1.web 请求设置withCredentials
这里默认情况下在跨域请求,浏览器是不带 cookie 的。但是我们可以通过设置 withCredentials 来进行传递 cookie.
// 原生 xml 的设置方式
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
// axios 设置方式
axios.defaults.withCredentials = true;
2.Access-Control-Allow-Credentials 为 true
3.Access-Control-Allow-Origin为非 *
2.Node 正向代理
服务器与服务器之间请求数据并不会存在跨域行为,跨域行为是浏览器安全策略限制
3.Nginx 反向代理
- 使用charles等正向代理方式比较简单,需要掌握的知识点也比较少。但相应的其可配置性较弱,仅适合中小型项目使用。
- 使用nginx的反向代理则相对复杂一些,需要了解基本的nginx配置。但其可配置性较强,支持URL的正则匹配,设置优先级等,适合复杂的项目使用。
4.JSONP
JSONP 主要就是利用了 script 标签没有跨域限制的这个特性来完成的。
-
使用限制
仅支持 GET 方法,如果想使用完整的 REST 接口,请使用 CORS 或者其他代理方式。
-
流程解析
1.前端定义解析函数(例如 jsonpCallback=function(){....})
2.通过 params 形式包装请求参数,并且声明执行函数(例如 cb=jsonpCallback)
3.后端获取前端声明的执行函数(jsonpCallback),并以带上参数并调用执行函数的方式传递给前端。
<script type="text/javascript">
window.jsonpCallback = function(res) {
console.log(res);
};
</script>
<script
src="http://localhost:8080/api/jsonp?msg=hello&cb=jsonpCallback"
type="text/javascript"
>
</script>
5.Websocket
WebSocket 规范定义了一种 API,可在网络浏览器和服务器之间建立“套接字”连接。简单地说:客户端和服务器之间存在持久的连接,而且双方都可以随时开始发送数据。
6.window.postMessage
-
页面和其打开的新窗口的数据传递
-
多窗口之间消息传递
-
页面与嵌套的 iframe 消息传递
7.document.domain + Iframe
「该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com 适用于该方式」。 只需要给页面添加 document.domain ='test.com' 表示二级域名都相同就可以实现跨域。
www. baidu. com .
三级域 二级域 顶级域 根域
// a.test.com
<body>
helloa
<iframe
src="http://b.test.com/b.html"
frameborder="0"
onload="load()"
id="frame"
></iframe>
<script>
document.domain = "test.com";
function load() {
console.log(frame.contentWindow.a);
}
</script>
</body>
// b.test.com
<body>
hellob
<script>
document.domain = "test.com";
var a = 100;
</script>
</body>
8.window.location.hash + Iframe
- 实现原理
原理就是通过 url 带 hash ,通过一个非跨域的中间页面来传递数据。
- 实现流程
一开始 a.html 给 c.html 传一个 hash 值,然后 c.html 收到 hash 值后,再把 hash 值传递给 b.html,最后 b.html 将结果放到 a.html 的 hash 值中。 同样的,a.html 和 b.htm l 是同域的,都是 http://localhost:8000,而 c.html 是http://localhost:8080
// a.html
<iframe src="http://localhost:8080/hash/c.html#name1"></iframe>
<script>
console.log(location.hash);
window.onhashchange = function() {
console.log(location.hash);
};
</script>
// b.html
<script>
window.parent.parent.location.hash = location.hash;
</script>
// c.html
<body></body>
<script>
console.log(location.hash);
const iframe = document.createElement("iframe");
iframe.src = "http://localhost:8000/hash/b.html#name2";
document.body.appendChild(iframe);
</script>
9.window.name + Iframe
window 对象的 name 属性是一个很特别的属性,当该 window 的 location 变化,然后重新加载,它的 name 属性可以依然保持不变。
其中 a.html 和 b.html 是同域的,都是http://localhost:8000,而 c.html 是http://localhost:8080
// a.html
<iframe
src="http://localhost:8080/name/c.html"
frameborder="0"
onload="load()"
id="iframe">
</iframe>
<script>
let first = true; // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
function load() {
if (first) { // 第1次onload(跨域页)成功后,切换到同域代理页面
iframe.src = "http://localhost:8000/name/b.html";
first = false;
} else { // 第2次onload(同域b.html页)成功后,读取同域window.name中数据
console.log(iframe.contentWindow.name);
}
}
</script>
b.html 为中间代理页,与 a.html 同域,内容为空。
// b.html
<div></div>
// c.html
<script>
window.name = "秋风的笔记";
</script>
通过 iframe 的 src 属性由外域转向本地域,跨域数据即由 iframe 的 window.name 从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。