1、概念
同源策略(即同端口、同协议、同域名): 同源策略阻止从一个域上加载的脚本获取或操作另一个域上的文档属性。也就是说,受到请求的 URL 的域必须与当前 Web 页面的域相同。这意味着浏览器隔离来自不同源的内容,以防止它们之间的操作。
http://www.yonyou.com | https://www.yonyou.com | 协议不同 |
http://www.zzl.com | http://www.michael.com | 域名不同 |
http://www.zzl.com:81/dir/etc.html | http://www.zzl.com/dir/etc.html | 端口不同 |
1> 目的:为了保证用户信息的安全,防止恶意的网站窃取用户数据。
2> 举例:设想这样一种情况:A 网站是一家银行,用户登录以后,A 网站在用户的机器上设置了一个 Cookie,包含了一些隐私信息(比如存款总额)。用户离开 A 网站以后,又去访问 B 网站,如果没有同源限制,B 网站可以读取 A 网站的 Cookie,那么隐私信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。由此可见,同源政策是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。
3> 同源策略限制范围:
1⃣️无法读取非同源网页的 cookie、LocalStorage 和 IndexedDB。
2⃣️ 无法接触非同源网页的 DOM。
3⃣️ AJAX无法请求非同源数据(可以发送,但浏览器会拒绝接受响应)
2、跨域方案(跟浏览器的同源策略作斗争,钻漏洞)
思路 : 每个元素的src不受同源策略限制,服务器之间访问不受同源策略限制
1> jsonp跨域----前端最常用的方法
本质:src不受同源策略限制,script用src加载过来的所有字符串,浏览器js解析器不是根据后缀名进行解析,而是根据script标签去解析,后端接口文件返回类似“fn(a)”前端函数调用的代码,想要传递的数据以参数形式发送给前端。
//jsonP的封装
function jsonp(url,cb){
var cbName = "callback" + new Date().getTime();
//避免缓存,每次回调函数名不同,相应url不同,请求新数据
window[cbName] = function(res){
cb(res)
}
var src = url+"?"+"cb="+cbName;
var script = document.createElement("script");
script.src = src;
document.body.appendChild(script);
script.onload = function(){ // 没有用了就删掉,释放内存空间
this.remove(); //删掉script标签
if(window[cbName]){ //函数若还存在,删除函数
setTimeout(function(){
delete window[cbName];
},100)}
}
}
以百度的搜索接口为例,先理清思路 :
1.准备接口 : https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=zzzzzz&pwd=zzzzz&cb=
2.动态创建script标签
3.设置src属性(通过script的src将后端接口文件引入)
4.将script标签添加到页面。
5.准备的回调函数一定是全局函数
//将script加入页面后,会自动调用传入的回调函数
贴上完整代码 :
<style>
* { padding:0; margin:0; font-size: 12px;}
#ul1 { list-style: none;}
li { border: 1px solid #ddd; width: 148px; text-align: center;}
li:hover { cursor: pointer;}
</style>
<input type="text" name="txt" id="txt" value="" />
<ul id="ul1"></ul>
<script type="text/javascript">
var oUl = document.getElementById("ul1");
var oTxt = document.getElementById("txt");
oTxt.onkeyup=function(){ //每变动一个字符,都会触发事件
oUl.innerHTML="";
var oScript=document.createElement("script");
oScript.src="https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd="+oTxt.value+"&cb=fn";
document.querySelector('head').appendChild(oScript);
//jsonp跨域,执行完这句代码后,自动调用回调函数
}
function fn(res){
for(var i=0;i<res.s.length;i++){ //在搜索框的下方,显示联想词
var oLi=document.createElement("li");
oLi.innerHTML=res.s[i];
oUl.appendChild(oLi);
}
var oLi=document.querySelectorAll("li");
oLi=Array.from(oLi);
for(var i=0;i<oLi.length;i++){
(function(i){
oLi[i].onmouseenter=function(){
this.style.background="#eee";
//鼠标移到联想词上时颜色变化
}
oLi[i].onmouseleave=function(){
this.style.background="#fff";
//鼠标移出联想词时颜色变回原样(js单线程执行)
}
oLi[i].onclick=function(){ //在某个关键词上点击时,搜索框内容变为这个关键词
oTxt.value=this.innerHTML;
oUl.innerHTML="";
}
})(i)
}
}
</script>
2> cors跨域(完全依赖后台实现的跨域,后端允许跨域)
3> 反向代理跨域(依赖服务器实现的跨域)
思路:浏览器请求同源服务器,再由后者请求外部服务。
1⃣️ 反向代理服务器跟目标服务器请求,获得所需的资源到反向代理器上,然会返回给浏览器。
2⃣️ nginx把从互联网资源上请求下来的内容放在自己的服务器里,然后用自己的服务器分发给局域网里的所有人。这种服务器数据请求模式,就叫做反向代理。
3⃣️ 接口写在Nginx文件下conf文件目录下,proxy-conf代理文件
4> Iframe跨域(唯一一个只依靠前端实现的跨域)
<iframe src="https://www.baidu.com/" frameborder="0"></iframe>
只有在同源的情况下,父窗口和子窗口才能通信;如果跨域,就无法拿到对方的 DOM。
5> domain降域跨域(cookie)(太古老,不推荐)
举例:如果两个网页一级域名相同,只是次级域名不同,浏览器允许通过设置document.domain共享 Cookie。