什么是跨域请求?
当浏览器执行一个脚本时会检查是否同源,只有同源的脚本才会执行,如果不同源即为跨域
什么是同源?
同源即:由Netscape提出的著名安全策略,是浏览器最核心、基本的安全功能,它限制了一个源(origin)中加载文本或者脚本与来自其他源(origin)中资源的交互方式
,所谓的同源就是指协议、域名、端口相同。
只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。
协议?域名?端口?
协议:网络协议遍及OSI通信模型(OSI七层模型,常用协议有TCP/IP、HTTP、FTP协议等)
域名:Domain Name,网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)
端口:是设备与外界通讯交流的出口,分为物理端口和虚拟端口(常见的如80端口)
了解概念后我们知道跨域请求就是web浏览器自身不允许在域名、协议、端口等都不相同的情况下进行页面请求方为,因此作为前端developer在项目开发时是需要解决此类问题的!
为什么要有这种限制?非同源请求页面会怎么样?
设想这样一个情景:A网站是一家银行,用户登录以后,又去浏览其他的网站B,如果网站B可以读取A网站的Cookie,会发生什么问题?
显然,如果Cookie包含隐私(比如存款总额),这些信息就会泄露,更可怕的是,Cookie往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒用户,为所欲为。因为浏览器同时还规定,提交表单不受同源策略的限制。
非同源限制范围
- Cookie、LocalStorage和IndexDB无法获取
- DOM无法获得
- AJAX请求不能发送
跨域请求解决方案
1. jsonp:(JSON with Padding是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题)
原理:网页客户端动态添加<script>标签添加src属性,向服务端发送json请求(不受同源策略束缚)服务器收到请求后,将数据放在一个指定名字的回调函数里(作为参数)传回来。
// 前端请求代码
//http://127.0.0.1:8888/jsonp.html
var script = document.createElement('script');
script.src = 'http://127.0.0.1:2333/jsonpHandler?callback=_callback'
document.body.appendChild(script); //插入script标签
//回调处理函数 _callback
var _callback = function(obj){
for(key in obj){
console.log('key: ' + key +' value: ' + obj[key]);
}
}
// 后端响应代码
//http://127.0.0.1:2333/jsonpHandler
app.get('/jsonpHandler', (req,res) => {
let callback = req.query.callback;
let obj = {
type : 'jsonp',
name : 'weapon-x'
};
res.writeHead(200, {"Content-Type": "text/javascript"});
res.end(callback + '(' + JSON.stringify(obj) + ')');
})
JSONP只支持Get请求方式
2.CORS:(跨域资源共享)是由W3C制定的跨站资源分享标准,可以让AJAX实现跨域访问,除了 GET 要求方法以外也支持其他的 HTTP 要求。服务器一般需要增加如下响应头的一种或几种:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
跨域请求默认不会携带Cookie信息,如果需要携带,请配置下述参数:
"Access-Control-Allow-Credentials": true
// Ajax设置
"withCredentials": true
3.window.name+iframe:利用iframe标签的跨域能力,window.name属性值在文档刷新后依旧存在的能力(且最大允许2M左右)
<!--
下述用端口
10000表示:domainA
10001表示:domainB
-->
<!-- localhost:10000 -->
<script>
var iframe = document.createElement('iframe');
iframe.style.display = 'none'; // 隐藏
var state = 0; // 防止页面无限刷新
iframe.onload = function() {
if(state === 1) {
console.log(JSON.parse(iframe.contentWindow.name));
// 清除创建的iframe
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
} else if(state === 0) {
state = 1;
// 加载完成,指向当前域,防止错误(proxy.html为空白页面)
// Blocked a frame with origin "http://localhost:10000" from accessing a cross-origin frame.
iframe.contentWindow.location = 'http://localhost:10000/proxy.html';
}
};
iframe.src = 'http://localhost:10001';
document.body.appendChild(iframe);
</script>
<!-- localhost:10001 -->
<!DOCTYPE html>
...
<script>
window.name = JSON.stringify({a: 1, b: 2});
</script>
</html>
4.window.name:window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
<script> //a页面设置
window.name="kowalski";
</script>
<script> //b页面取出
alert(window.name);
</script>
正在接触后续补充...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。