什么是跨域
注:本文完全示例地点
先来讲一个观点就是同源,同源指的是协定,端口,域名悉数雷同。
同源战略(Same origin policy)是一种商定,它是浏览器最中心也最基本的平安功用,假如缺少了同源战略,则浏览器的平常功用能够都邑遭到影响。能够说Web是构建在同源战略基本之上的,浏览器只是针对同源战略的一种完成。
同源战略是处于对用户平安的斟酌,假如非同源就会遭到以下限定:
cookie不能读取
dom没法取得
ajax要求不能发送
然则事实是常常须要借助非同源来供应数据,所以就须要举行跨域要求。
JSONP
JSONP
是指JSON Padding
,JSONP
是一种非官方跨域数据交换协定,因为script
的src
属性能够跨域要求,所以JSONP
应用的就是浏览器的这个“破绽”,须要通讯时,动态的插进去一个script
标签。要求的地点平常带有一个callback
参数,假定须要要求的地点为http://localhost:666?callback=show
,服务端返回的代码平常是show(数据)
的JSON
数据,而show
函数恰恰是前台须要用这个数据的函数。JSONP
异常的简朴易用,自动补全API应用的就是JSONP
,下面来看一个例子:
// 前端要求代码
function jsonp (callback) {
var script = document.createElement("script"),
url = `http://localhost:666?callback=${callback}`;
script.setAttribute("src", url);
document.querySelector("head").appendChild(script);
}
function show (data) {
console.log(`门生姓名为:${data.name},岁数为:${data.age},性别为${data.sex}`);
}
jsonp("show");
// 后端相应代码
const student = {
name: "zp1996",
age: 20,
sex: "male"
};
var callback = url.parse(req.url, true).query.callback;
res.writeHead(200, {
"Content-Type": "application/json;charset=utf-8"
});
res.end(`${callback}(${JSON.stringify(student)})`);
JSONP
虽然说简朴易用,然则有一个很大题目,那就是JSONP
只能举行get
要求
CORS
CORS(跨域资本共享)是由W3C制订的跨站资本分享规范,能够让AJAX
完成跨域接见。想要相识跨域的话,起首须要相识下简朴要求:
要求体式格局为
GET
或许POST
倘使要求是
POST
的话,Content-Type
必需为以下之一:application/x-www-form-urlencoded
multipart/form-data
text/plain
不含有自定义头(类似于segmentfault自定义的头
X-Hit
)
关于简朴要求的跨域只须要举行一次http
要求:
function ajaxPost (url, obj, header) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest(),
str = '',
keys = Object.keys(obj);
for (var i = 0, len = keys.length; i < len; i++) {
str += `${keys[i]}=${obj[keys[i]]}&`;
}
str = str.substring(0, str.length - 1);
xhr.open('post', url);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
if (header instanceof Object) {
for (var k in header)
xhr.setRequestHeader(k, header[k]);
}
xhr.send(str);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
resolve(xhr.responseText);
} else {
reject();
}
}
}
});
}
ajaxPost("http://localhost:666?page=cors", {
name: "zp1996",
age: 20,
sex: "male"
})
.then((text) => { console.log(text); },
() => { console.log("要求失利"); });
// 后端处置惩罚
var postData = "";
// 诠释下,下面示例背景代码补充在此处
req.on("data", (data) => {
postData += data;
});
req.on("end", () => {
postData = querystring.parse(postData);
res.writeHead(200, {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json;charset=utf-8"
});
if (postData.name === student.name &&
Number(postData.age) === student.age &&
postData.sex === student.sex
) {
res.end(`yeah!${postData.name} is a good boy~`);
} else {
res.end("No!a bad boy~");
}
});
翻开控制台视察能够发明,Network
只是发出了一次要求,然则关于非简朴要求来讲,须要两次http
要求,在真正的要求之前须要举行一次预要求,下图是举行一次预要求的要求/相应:
视察相应头,能够发明须要多出了两个相应头:
Access-Control-Allow-Headers
,用来指明在现实的要求中,能够运用那些自定义的http
要求头。Access-Control-Max-Age
,用来指定此次预要求的效果的有效期,在有效期内则不会发出预要求,有点像缓存的觉得。
固然另有诸如很多如许的相应头,请人人自行搜刮相识,这里就不再过量引见,下面来看下关于非简朴要求跨域的代码处置惩罚:
// 前端要求代码
ajaxPost("http://localhost:666?page=cors", {
name: "zp1996",
age: 20,
sex: "male"
}, { "X-author": "zp1996" })
.then((text) => { console.log(text); },
() => { console.log("要求失利"); });
// 后端处置惩罚,补充在简朴要求代码诠释处
if (req.method === "OPTIONS") {
res.writeHead(200, {
"Access-Control-Max-Age": 3000,
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "X-author",
"Content-Type": "application/json;charset=utf-8"
});
res.end();
return void 0;
}
CSS Text Transformation
既然能够应用script
的“破绽”来举行JSONP
跨域,那末是否是也能够应用css
款式写能够举行跨域要求来举行跨域呢?答案肯定是yes,应用css
另有一个优点那就是,当被注入进击剧本时,css
只管被注入,也不会引发什么大的平安题目,顶多也就是把页面的款式给转变,而js
被注入的话,cookie
就有能够被偷取等一系列平安题目涌现。大牛已将其做的异常完美,人人能够去star王集鹄(zswang)CSST,这里我就把我所明白给人人简朴的分享下:
// 前端代码
const id = "csst",
ele = document.querySelector(`#${id}`),
head = document.querySelector("head");
function getStyle (ele, prop) {
return getComputedStyle(ele, "").getPropertyValue(prop);
}
function loadCss (url) {
return new Promise((resolve) => {
const link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("type", "text/css");
link.setAttribute("href", url);
ele.addEventListener("webkitAnimationStart", function () {
resolve(getStyle(ele, "content"));
});
head.appendChild(link);
});
}
loadCss(`http://localhost:666?page=data.css&id=${id}`).then((data) => {
console.log(data);
});
// 后端代码
function cssData (id) {
return `
@keyframes a{
from{
}
to{
color: red;
}
}
#${id} {
content: "这类是很好,然则只能传输文本啊";
animation: a 2s;
}
`;
}
res.writeHead(200, {
"Content-Type": "text/css"
});
res.end(cssData(query.id));
经由过程代码能够看出这类完成体式格局是靠元素的content
来拿吸收到的数据,所以传输的只能是文本。至于为何要返回动画?是因为不应用动画,没法来对css
剧本加载举行监测,也就没法举行回调(因为谷歌/火狐不支撑link
的onload
和onreadychange
,所以应用animationstart
事宜)。
window.postMessage
window.postMessage 是一个平安的跨源通讯的要领。平常情况下,当且仅当实行剧本的页面运用雷同的协定(一般都是 http)、雷同的端口(http默许运用80端口)和雷同的 host(两个页面的 document.domain 的值雷同)时,才许可差别页面上的剧本相互接见。 window.postMessage 供应了一个可控的机制来平安地绕过这一限定,当其在准确运用的情况下。
window.postMessage
处理的不是浏览器与服务器之间的交互,处理的是浏览器差别的窗口之间的通讯题目,能够做的就是同步两个网页,固然这两个网页应该是属于同一个基本域名。
// 发送端代码
var domain = "http://localhost",
index = 1,
target = window.open(`${domain}/postmessage-target.html`);
function send () {
setInterval(() => {
target.postMessage(`第${index++}次数据发送`, domain);
}, 1000);
}
send();
// 吸收端代码
<div id="test">没有数据过来啊<div>
<script type="text/javascript">
var test = document.querySelector("#test");
window.addEventListener("message", e => {
if (e.origin !== "http://localhost") {
return void 0;
}
test.innerText = e.data;
});
</script>
上述代码完成了向一个页面向另一个发送数据,然则这么写每每有着一些“风险”,须要晓得的是,postMessage
是向document
对象中,网络连接有时会很慢,能够会涌现些题目,所以最好的体式格局是吸收页面已最先加载了,这时候发送一个音讯给发送端,发送端在最先向吸收端发送数据。革新下:
// 发送端增加代码
window.addEventListener("message", (e) => {
if (e.data === "ok")
send();
else
console.log(e.data);
});
// 吸收端的head内里加上script标签
<script type="text/javascript">
opener.postMessage("ok", opener.domain);
</script>
window.name
window.name 的美好的地方:name 值在差别的页面(以至差别域名)加载后照旧存在,而且能够支撑异常长的 name 值(2MB)
这个体式格局我基本上没有用过,所以没有过量的发言权,人人想相识这个手艺的话,能够经由过程怿飞(圆心):运用 window.name 处理跨域题目,圆心大神诠释的异常透辟。
document.domain
将子域和主域的document.domain
设为同一个主域
前提条件:
这两个域名必需属于同一个基本域名
而且所用的协定,端口都要一致,不然没法应用
document.domain
举行跨域