Qu'est-ce que le cross-domain ?
Cross-domain fait référence à un document ou à un script dans un domaine essayant de demander des ressources dans un autre domaine. Ici, le cross-domain est large.
Cross-domaine au sens large :
1 2 3 | 1.) 资源跳转: A链接、重定向、表单提交
2.) 资源嵌入: <link>、<script>、<img>、<frame>等dom标签,还有样式中background:url()、@font-face()等文件外链
3.) 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等
|
Copier après la connexion
En fait, ce que nous appelons habituellement cross-domain est au sens étroit et est un type de scénario de requête limité par la politique de même origine du navigateur.
Quelle est la même politique d'origine ?
SOP (Same origin Policy) est une convention introduite dans le navigateur par Netscape en 1995. Il s'agit de la fonction de sécurité principale et la plus élémentaire du navigateur. Si la même politique d'origine est manquante, la politique d'origine. , les navigateurs sont vulnérables aux attaques XSS, CSFR et autres. La soi-disant même origine signifie que « protocole + nom de domaine + port » sont identiques. Même si deux noms de domaine différents pointent vers la même adresse IP, ils ne proviennent pas de la même origine.
La politique de même origine restreint les comportements suivants :
1 2 3 | 1.) Cookie、LocalStorage 和 IndexDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送
|
Copier après la connexion
Scénarios inter-domaines courants
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | URL 说明 是否允许通信
http:
http:
http:
http:
http:
http:
https:
http:
http:
http:
http:
http:
http:
http:
|
Copier après la connexion
Solutions inter-domaines
1. Inter-domaine via jsonp
2. Document.domain + iframe cross-domain location.hash + iframe
4.
5. PostMessage inter-domaine
6. Partage de ressources inter-domaines (CORS)
7. Protocole nginx inter-domaine
8. cross-domain
1. Cross-domain via jsonpHabituellement afin de réduire la charge sur le serveur web, nous séparons les ressources statiques telles que js, css, img, etc. sur un autre serveur avec un nom de domaine indépendant, et dans la page html, China Re charge des ressources statiques de différents noms de domaine via les balises correspondantes et est autorisé par le navigateur. Sur la base de ce principe, nous pouvons créer dynamiquement des scripts puis demander. une URL avec des paramètres pour réaliser une communication inter-domaines.
1.) Implémentation native :
1 2 3 4 5 6 7 8 9 10 11 12 13 | <script>
var script = document.createElement(& #39;script');
script.type = & #39;text/javascript';
script.src = & #39;http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
document.head.appendChild(script);
function handleCallback(res) {
alert(JSON.stringify(res));
}
</script>
|
Copier après la connexion
Le serveur renvoie comme suit (la fonction globale est exécutée lors du retour) :
1 | handleCallback({ "status" : true , "user" : "admin" })
|
Copier après la connexion
2.) jquery ajax :
1 2 3 4 5 6 7 | $.ajax({
url: & #39;http://www.domain2.com:8080/login',
type: & #39;get',
dataType: & #39;jsonp', // 请求方式为jsonp
jsonpCallback: "handleCallback" ,
data: {}
});
|
Copier après la connexion
3.) vue.js :
1 2 3 4 5 6 | this .$http.jsonp(& #39;http://www.domain2.com:8080/login', {
params: {},
jsonp: & #39;handleCallback'
}).then((res) => {
console.log(res);
})
|
Copier après la connexion
Exemple de code backend node.js :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var querystring = require(& #39;querystring');
var http = require(& #39;http');
var server = http.createServer();
server.on(& #39;request', function(req, res) {
var params = qs.parse(req.url.split(& #39;?')[1]);
var fn = params.callback;
res.writeHead(200, { & #39;Content-Type': 'text/javascript' });
res.write(fn + & #39;(' + JSON.stringify(params) + ')');
res.end();
});
server.listen(& #39;8080');
console.log(& #39;Server is running at port 8080...');
|
Copier après la connexion
jsonp Inconvénients : Un seul type de requête get peut être implémenté.
2. document.domain + iframe cross-domain Cette solution est limitée aux scénarios d'application inter-domaines où le domaine principal est le même mais les sous-domaines sont différents .
Principe de mise en œuvre : les deux pages définissent de force document.domain comme domaine principal de base via js, obtenant ainsi le même domaine.
1.) Fenêtre parent : (http://www.domain.com/a.html)
1 2 3 4 5 | <iframe id= "iframe" src= "http://child.domain.com/b.html" ></iframe>
<script>
document.domain = & #39;domain.com';
var user = & #39;admin';
</script>
|
Copier après la connexion
2.) Fenêtre enfant : (http://child.domain.com /b.html)
1 2 3 4 5 | <script>
document.domain = & #39;domain.com';
alert(& #39;get js data from parent ---> ' + window.parent.user);
</script>
|
Copier après la connexion
3. location.hash + iframe cross-domain Principe de mise en œuvre : a veut communiquer avec b à travers les domaines via la page intermédiaire c accomplir. Pour trois pages, le location.hash de l'iframe est utilisé pour transférer des valeurs entre différents domaines, et l'accès direct js est utilisé pour communiquer entre les mêmes domaines.
Implémentation spécifique : Domaine A : a.html -> Domaine B : b.html -> Domaine A : c.html, les domaines a et b ne peuvent communiquer que dans un sens via des valeurs de hachage, b et c est également dans un domaine différent et ne peut communiquer que dans une seule direction, mais c et a sont dans le même domaine, donc c peut accéder à tous les objets de la page a via parent.parent.
1.) a.html : (http://www.domain1.com/a.html)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <iframe id= "iframe" src= "http://www.domain2.com/b.html" style= "display:none;" ></iframe>
<script>
var iframe = document.getElementById(& #39;iframe');
setTimeout( function () {
iframe.src = iframe.src + & #39;#user=admin';
}, 1000);
function onCallback(res) {
alert(& #39;data from c.html ---> ' + res);
}
</script>
|
Copier après la connexion
2.) b.html : (http://www.domain2 .com/b.html)
1 2 3 4 5 6 7 | <script>
window.onhashchange = function () {
window.parent.parent.onCallback(& #39;hello: ' + location.hash.replace('#user=', ''));
};
</script>
|
Copier après la connexion
Copier après la connexion
3.) c.html : (http://www.domain1.com/c.html)
1 2 3 4 5 6 7 | <script>
window.onhashchange = function () {
window.parent.parent.onCallback(& #39;hello: ' + location.hash.replace('#user=', ''));
};
</script>
|
Copier après la connexion
Copier après la connexion
4. name + iframe cross-domain Le caractère unique de l'attribut window.name : la valeur du nom existe toujours après le chargement de différentes pages (ou même de différents noms de domaine), et peut prendre en charge des valeurs de nom très longues (2Mo) .
1.) a.html : (http://www.domain1.com/a.html)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | var proxy = function (url, callback) {
var state = 0;
var iframe = document.createElement(& #39;iframe');
iframe.src = url;
iframe.onload = function () {
if (state === 1) {
callback(iframe.contentWindow.name);
destoryFrame();
} else if (state === 0) {
iframe.contentWindow.location = & #39;http://www.domain1.com/proxy.html';
state = 1;
}
};
document.body.appendChild(iframe);
function destoryFrame() {
iframe.contentWindow.document.write(& #39;');
iframe.contentWindow.close();
document.body.removeChild(iframe);
}
};
proxy(& #39;http://www.domain2.com/b.html', function(data){
alert(data);
});
|
Copier après la connexion
2.) proxy.html : (http://www.domain1 .com/proxy....
La page proxy intermédiaire est dans le même domaine que a.html, et le contenu peut être vide
3.) b.html : (http://. www.domain2.com/b.html)
1 2 3 | <script>
window.name = & #39;This is domain2 data!';
</script>
|
Copier après la connexion
Résumé : l'attribut src de l'iframe est transféré du domaine externe au domaine local et les données inter-domaines sont transmises du domaine externe au domaine local. domaine local par le window.name de l'iframe. Cela contourne intelligemment les restrictions d'accès inter-domaines du navigateur, mais en même temps, il s'agit d'une opération sûre.
5. postMessage inter-domaines postMessage est une API en HTML5 XMLHttpRequest niveau 2 et est l'un des rares attributs de fenêtre pouvant être utilisé sur plusieurs domaines. est disponible Pour résoudre les problèmes suivants :
a.) Transfert de données entre les pages et les nouvelles fenêtres qu'elles ouvrentb.) Transfert de messages entre plusieurs fenêtres
c.) Transfert de messages entre les pages et les iframes imbriquées
d.) Transfert de données entre domaines dans les trois scénarios ci-dessus
Utilisation : la méthode postMessage(data, origin) accepte deux paramètres
data : La spécification html5 prend en charge tout type de base ou objet copiable, cependant, certains les navigateurs ne prennent en charge que les chaînes, il est donc préférable d'utiliser la sérialisation JSON.stringify() lors du passage des paramètres. origine : protocole + hôte + numéro de port, il peut également être défini sur "*", ce qui signifie qu'il peut être transmis à n'importe quelle fenêtre. Si vous souhaitez spécifier la même origine que la fenêtre actuelle, définissez-la sur "/". ".
1.) a.html : (http://www.domain1.com/a.html)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <iframe id= "iframe" src= "http://www.domain2.com/b.html" style= "display:none;" ></iframe>
<script>
var iframe = document.getElementById(& #39;iframe');
iframe.onload = function () {
var data = {
name: & #39;aym'
};
iframe.contentWindow.postMessage(JSON.stringify(data), & #39;http://www.domain2.com');
};
window.addEventListener(& #39;message', function(e) {
alert(& #39;data from domain2 ---> ' + e.data);
}, false );
</script>
|
Copier après la connexion
2.) b.html : (http://www.domain2 .com/b.html)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <script>
window.addEventListener(& #39;message', function(e) {
alert(& #39;data from domain1 ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
window.parent.postMessage(JSON.stringify(data), & #39;http://www.domain1.com');
}
}, false );
</script>
|
Copier après la connexion
六、 跨域资源共享(CORS)
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
需注意的是:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。如果想实现当前页cookie的写入,可参考下文:七、nginx反向代理中设置proxy_cookie_domain 和 八、NodeJs中间件代理中cookieDomainRewrite参数的设置。
目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。
1、 前端设置:
1.)原生ajax
1 2 | xhr.withCredentials = true ;
|
Copier après la connexion
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var xhr = new XMLHttpRequest();
xhr.withCredentials = true ;
xhr.open(& #39;post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader(& #39;Content-Type', 'application/x-www-form-urlencoded');
xhr.send(& #39;user=admin');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
|
Copier après la connexion
2.)jQuery ajax
1 2 3 4 5 6 7 8 | $.ajax({
...
xhrFields: {
withCredentials: true
},
crossDomain: true ,
...
});
|
Copier après la connexion
3.)vue框架
a.) axios设置:
1 | axios.defaults.withCredentials = true
|
Copier après la connexion
b.) vue-resource设置:
1 | Vue.http.options.credentials = true
|
Copier après la connexion
2、 服务端设置:
若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,说明没设成功。
1.)Java后台:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
response.setHeader( "Access-Control-Allow-Origin" , "http://www.domain1.com" );
response.setHeader( "Access-Control-Allow-Credentials" , "true" );
response.setHeader( "Access-Control-Allow-Headers" , "Content-Type,X-Requested-With" );
|
Copier après la connexion
2.)Nodejs后台示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | var http = require(& #39;http');
var server = http.createServer();
var qs = require(& #39;querystring');
server.on(& #39;request', function(req, res) {
var postData = & #39;';
req.addListener(& #39;data', function(chunk) {
postData += chunk;
});
req.addListener(& #39;end', function() {
postData = qs.parse(postData);
res.writeHead(200, {
& #39;Access-Control-Allow-Credentials': 'true', // 后端允许发送Cookie
& #39;Access-Control-Allow-Origin': 'http://www.domain1.com', // 允许访问的域(协议+域名+端口)
& #39;Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly的作用是让js无法读取cookie
});
res.write(JSON.stringify(postData));
res.end();
});
});
server.listen(& #39;8080');
console.log(& #39;Server is running at port 8080...');
|
Copier après la connexion
七、 nginx代理跨域
1、 nginx配置解决iconfont跨域
浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置。
1 2 3 | location / {
add_header Access-Control-Allow-Origin *;
}
|
Copier après la connexion
2、 nginx反向代理接口跨域
跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
nginx具体配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http:
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http:
add_header Access-Control-Allow-Credentials true ;
}
}
|
Copier après la connexion
1.) 前端代码示例:
1 2 3 4 5 6 7 8 | var xhr = new XMLHttpRequest();
xhr.withCredentials = true ;
xhr.open(& #39;get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
|
Copier après la connexion
2.) Nodejs后台示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | var http = require(& #39;http');
var server = http.createServer();
var qs = require(& #39;querystring');
server.on(& #39;request', function(req, res) {
var params = qs.parse(req.url.substring(2));
res.writeHead(200, {
& #39;Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:脚本无法读取
});
res.write(JSON.stringify(params));
res.end();
});
server.listen(& #39;8080');
console.log(& #39;Server is running at port 8080...');
|
Copier après la connexion
八、 Nodejs中间件代理跨域
node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。
1、 非vue框架的跨域(2次跨域)
利用node + express + http-proxy-middleware搭建一个proxy服务器。
1.)前端代码示例:
1 2 3 4 5 6 7 8 | var xhr = new XMLHttpRequest();
xhr.withCredentials = true ;
xhr.open(& #39;get', 'http://www.domain1.com:3000/login?user=admin', true);
xhr.send();
|
Copier après la connexion
2.)中间件服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | var express = require(& #39;express');
var proxy = require(& #39;http-proxy-middleware');
var app = express();
app.use(& #39;/', proxy({
target: & #39;http://www.domain2.com:8080',
changeOrigin: true ,
onProxyRes: function (proxyRes, req, res) {
res.header(& #39;Access-Control-Allow-Origin', 'http://www.domain1.com');
res.header(& #39;Access-Control-Allow-Credentials', 'true');
},
cookieDomainRewrite: & #39;www.domain1.com' // 可以为false,表示不修改
}));
app.listen(3000);
console.log(& #39;Proxy server is listen at port 3000...');
|
Copier après la connexion
3.)Nodejs后台同(六:nginx)
2、 vue框架的跨域(1次跨域)
利用node + webpack + webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了。
webpack.config.js部分配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true ,
proxy: [{
context: & #39;/login',
target: & #39;http://www.domain2.com:8080', // 代理跨域目标接口
changeOrigin: true ,
secure: false ,
cookieDomainRewrite: & #39;www.domain1.com' // 可以为false,表示不修改
}],
noInfo: true
}
}
|
Copier après la connexion
九、 WebSocket协议跨域
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
1.)前端代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <p>user input:<input type= "text" ></p>
<script src= "https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js" ></script>
<script>
var socket = io(& #39;http://www.domain2.com:8080');
socket.on(& #39;connect', function() {
socket.on(& #39;message', function(msg) {
console.log(& #39;data from server: ---> ' + msg);
});
socket.on(& #39;disconnect', function() {
console.log(& #39;Server socket has closed.');
});
});
document.getElementsByTagName(& #39;input')[0].onblur = function() {
socket.send( this .value);
};
</script>
|
Copier après la connexion
2.)Nodejs socket后台:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | var http = require(& #39;http');
var socket = require(& #39;socket.io');
var server = http.createServer( function (req, res) {
res.writeHead(200, {
& #39;Content-type': 'text/html'
});
res.end();
});
server.listen(& #39;8080');
console.log(& #39;Server is running at port 8080...');
socket.listen(server).on(& #39;connection', function(client) {
client.on(& #39;message', function(msg) {
client.send(& #39;hello:' + msg);
console.log(& #39;data from client: ---> ' + msg);
});
client.on(& #39;disconnect', function() {
console.log(& #39;Client socket has closed.');
});
});
|
Copier après la connexion
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!