一、同源策略
1.1同源
源:控制台打出window.origin或者location.origin,源=协议+域名+端口号
如果两个url的协议、域名、端口号一致,那么这两个url同源
https://baidu.com和https://www.baidu.com不同源
1.2源策略
浏览器规定如果JS运行在源A里面,只能获取源A的数据,不能获取源B的数据,即不允许跨域,这是浏览器故意涉及的,目的是为了保护用户的隐私
二、跨域
2.1CORS
浏览器默认不同源之间不能互相访问数据,但是如果要共享数据,需要在响应头提前声明
Access-Control-Allow-Origin:http://xxx.com //指定xxx.com可以访问
2.2JSONP
2.2.1流程
定义:跨域的时候,当前浏览器不支持CORES(例如 IE),不能访问JSON(数据),于是请求JS文件,JS文件会执行回调,回调会需要的数据
//源A服务器设置如果访问的是friends.js文件
if (path === "/friends.js") {
if (request.headers["referer"].indexOf("http://frank.com:9990") === 0) {
response.statusCode = 200;
response.setHeader("Content-Type", "text/javascript;charset=utf-8");
const string = fs.readFileSync("./public/friends.js").toString() //读取JS文件内容
const data = fs.readFileSync("./public/friends.json").toString();//读取数据
const string2 = string.replace("{{data}}", data)//将JS文件内容替换成JSON数据
response.write(string2);
response.end();
}
friends.js文件内容:
window.xxx({{data}}) //定义一个函数,{{data}}是占位符,表示会被替换
然后浏览器或者说源B就可以通过script标签获取friends.js文件
//动态添加script标签
const script = document.createElement('script')
script.src='http://xxx.com/friends.js'
document.body.appendChild(script)
window.xxx =(data)=>{ //window.xxx是一个回调,等待源A调用把数据传给源B
console.log(data)
}
2.2.2通过ref限制可以跨域的域名
if (path === "/friends.js") {
//如果请求来源的域名为frank.com则执行请求
if (request.headers["referer"].indexOf("http://frank.com:9990") === 0) {
response.statusCode = 200;
response.setHeader("Content-Type", "text/javascript;charset=utf-8");
const string = fs.readFileSync("./public/friends.js").toString() //读取JS文件内容
const data = fs.readFileSync("./public/friends.json").toString();//读取数据
const string2 = string.replace("{{data}}", data)
response.write(string2);
response.end();
} else {//如果请求来源的域名不为frank.com则返回404
response.statusCode = 404;
response.end();
}
}
2.2.3回调的名字随机生成
- 源B的
xxx(函数名)替换成随机数,通过查询参数的形式将这个随机数(函数名)传给源A
//动态添加script标签
const random = 'JSONPCallbackName'+Math.random()
const script = document.createElement('script')
script.src='http://xxx.com/friends.js?callback=${random}' //查询参数发送函数名
document.body.appendChild(script)
script.onload=()=>{
script.remove() //移除页面中的script标签
}
window.[random] =(data)=>{ //window.xxx是一个回调,等待源A调用把数据传给源B
console.log(data)
}
- 源A接受查询参数
if (path === "/friends.js") {
if (request.headers["referer"].indexOf("http://frank.com:9990") === 0) {
response.statusCode = 200;
response.setHeader("Content-Type", "text/javascript;charset=utf-8");
const string = `window['{{xxx}}']({{data}})`
const data = fs.readFileSync("./public/friends.json").toString();
const string2 = string.replace("{{data}}", data).replace('{{xxx}}', query.callback);//讲函数名xxx替换成查询参数的functionName
response.write(string2);
response.end();
} else {
response.statusCode = 404;
response.end();
}
}
2.2.4封装JSONP
function jsonp(url) {
return new Promise((resolve, reject) => {
const random = "JSONPCallbackName" + Math.random(); //声明一个随机的函数名
window[random] = data => {//声明函数
resolve(data);//成功的回调
};
const script = document.createElement("script");//动态添加script标签
script.src = `${url}?callback=${random}`;
script.onload = () => {
script.remove();
};
script.onerror = () => {//失败的回调
reject();
};
document.body.appendChild(script);
});
}
jsonp("http://xxx.com/friends.js").then(data => {
console.log(data);
});
2.2.5JSONP的优点
- 兼容IE
- 可以跨域,即使请求的
JSONP不是当前的域名,而是另外的域名,也可以成功的跨域
2.2.6JSONP的缺点
- 由于是script标签,不知道状态码是什么,也不知道响应头,只知道成功或是失败
- 由于是script标签,只能发送get请求,不能发送post请求