题目1: 什么是同源策略
- 同源策略(Same Origin Policy): 浏览器出于安全方面的考虑, 只允许与本域下的接口交互. 不同源的客户端脚本在没有明确授权的情况下, 不能读写对方的资源.
- 本域指同域名同协议同端口,即URL完全相同
- 注意: 对于当前页面来说页面存放的Js文件不重要, 重要的是加载该js页面所在什么域
题目2: 什么是跨域?跨域有几种实现形式
- 跨域: 利用JS在不同域进行数据的传输或通信, 例如用ajax向一个不同的域请求数据, 或者通过JS获取页面中不同域的iframe中的数据
- 实现形式:
- JSONP: 利用引入JS文件不受同源策略限制的特性, 前后端配合实现跨域访问接口
- CORS: W3C标准中的一个, 允许跨域发出xmlhttprequest请求, 需要服务端配合实现
- 降域: 修改document.domain, 将其设置为更高一级的父域, 从而符合同源策略
- postMessage
- NGINX代理
- node.js中间件
- webSocke协议跨域
题目3: JSONP 的原理是什么
JSONP就是通过script标签加载数据的方式去获取数据当做JS代码来执行; 提前在页面上声明一个函数, 函数名通过接口传参的方式传给后台, 后台解析到函数名后在原始数据上包裹这个函数名, 发送给前端
题目4: CORS是什么
- CORS(Cross-Origin Resource Sharing) 跨域资源共享, 是一种ajax跨域请求资源的方式, 支持现代浏览器,IE10以上
- 实现方式: 当使用XMLHTTPRequest发送请求上, 浏览器发现该请求不符合同源策略, 会给该请求加上一个请求头: Origin, 后台进行一系列处理, 如果确定接收请求则在返回结果中加一个响应头: Access-Control-Allow-Origin; 浏览器判断该响应头中是否包含Origin的值, 如果有则浏览器处理响应, 我们可以拿到响应数据, 如果不包含浏览器直接驳回, 这时我们无法拿到响应数据
- CORS表象让人感觉他与同源ajax请求没啥区别, 代码完全一样
浏览器将CORS请求分为两类:简单请求和非简单请求
只要同时满足下面两大条件,就属于简单请求
- 请求方法是以下三种方法之一:
HEAD
GET
POST- HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
不能同时满足上面2个条件的,就属于非间请求
比如对server端有特殊要求的请求,请求方法是PUT或者DELETE、content-type字段类型是application/json的
题目5: 根据视频里的讲解演示三种以上跨域的解决方式 ,写成博客
-
JSONP
通常为了减轻web服务器的负载, 我们把js, css, img等静态资源分离到另一台独立域名的服务器上,在HTML页面中再通过相应的标签从不同域名下加载静态资源, 而浏览器允许, 基于此原理, 我们可以通过动态创建script, 再请求一个带参网址实现跨域通信.- 原生实现
var script = document.creatElement('script'); script.type = 'text/javascript'; script.src = 'http://www.domain2.com:8080/login? user=admin&callback=onBack'; document.body.appendChild(script); function onBack(res){ alert(JSON.stringify(res)); } 服务器返回 onBack({"status":true, "user":"admin"})
- jquery ajax:
$.ajax({ url:'http://www.domain2.com:8080/login', type:'get', dataType:'jsonp',// 请求方式为jsonp jsonpCallback:"onBack",// 自定义回调函数名 data:{} });
后端node.js代码示例:
var querystring = require('querystring');
var http = require('http');
var server = http.createServer();
server.on('request',function(req,res){
varparams = qs.parse(req.url.split('?')[1]);
var fn = params.callback;
// jsonp返回设置
res.writeHead(200,{'Content-Type':'text/javascript'});
res.write(fn+'('+JSON.stringify(params)+')');
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
jsonp缺点:只能实现get一种请求。
- 降域
此方案仅限主域相同, 子域不同的跨域应用场景.
实现原理: 两个页面都通过js强制设置document.domain为基础主域, 就实现了同域.
1. 父窗口: (http://www.domain.com/a.html)
document.domain = 'domain.com'
var user = 'admin'
2. 子窗口: (http://child.domain.com/b.html)
document.domain = 'domain.com'
alert('get js data from parent' + window.parent.user)
// 获取父窗口中变量
- location.hash + iframe跨域
实现原理: a域与b域相互通信, 通过中间页c来实现. 三个页面, 不同域之间利用iframe的location.hash传值, 相同域之间直接js访问来通信
具体实现: a域: a.html > b域: b.html > a域: c.html , ab不同域只能通过hash值单向通信, bc也不同域只能单向通信, 但ac同域, 所以c可通过parent.parent访问a页面所有对象
1. a.html: (http://www.domain1.com/a.html)
var iframe = document.getElementById('iframe')
setTimeout(function(){
iframe.src = iframe.src + '#user=admin'
}, 1000); // 向b.html传hash值
functionCallback(res){
alert('data from c.html' + res)
}// 开放给同域c.html的回调方法
2. b.html: (http://www.domain2.com/b.html)
var iframe = document.getElementById('iframe');
window.onhashchange = function(){
iframe.src += location.hash;
}// 监听a.html传来的hash值,再传给c.html
3. c.html:(http://www.domain1.com/c.html)
window.onhashchange = function(){// 监听b.html传来的hash值
window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=',''));
}
- postMessage
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
a.) 页面和其打开的新窗口的数据传递
b.) 多窗口之间消息传递
c.) 页面与嵌套的iframe消息传递
d.) 上面三个场景的跨域数据传递
用法:postMessage(data,origin)方法接受两个参数
data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
origin: 协议+主机+端口号,也可以设置为”*”,表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/”。
1. a.html:(http://www.domain1.com/a.html)
var iframe = document.getElementById('iframe');
iframe.onload = function(){
var data = {name:'aym'};
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
}
window.addEventListener('message', function(e){
alert('data form domain2 ' + e.data);
}, false);
2. b.html:(http://www.domain2.com/b.html)
window.addEventListener('message', function(e){
alert('data from domain1 ' + e.data);
var data = JSON.parse(e.data);
if(data){
data.number = 16;
window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
}
}, false);
- 跨域资源共享(CORS)
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置。
带cookie请求:前后端都需要设置字段,另外需注意:所带cookie为跨域请求接口所在域的cookie,而非当前页。
目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。
1. 前端设置:
ajax:
var xhr=newXMLHttpRequest();// IE8/9需用window.XDomainRequest兼容
xhr.withCredentials=true;// 前端设置是否带cookie
xhr.open('post','http://www.domain2.com:8080/login',true);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onreadystatechange=function(){
if(xhr.readyState==4&&xhr.status==200){
alert(xhr.responseText);
}
};
2. Nodejs后台示例:
var http=require('http');
var server=http.createServer();
var qs=require('querystring');
server.on('request',function(req,res){
var postData='';
req.addListener('data',function(chunk){
postData+=chunk;
});// 数据块接收中
req.addListener('end',function(){
postData=qs.parse(postData);
// 跨域后台设置
res.writeHead(200,{
'Access-Control-Allow-Credentials':'true',// 后端允许发送Cookie
'Access-Control-Allow-Origin':'http://www.domain1.com',// 允许访问的域(协议+域名+端口)
'Set-Cookie':'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'// HttpOnly:脚本无法读取cookie
});
res.write(JSON.stringify(postData));
res.end();
});
});
server.listen('8080');
console.log('Server is running at port 8080...');