[Turn] front-end common cross-domain solutions (full)

Original Address: https://segmentfault.com/a/1190000012469713

What is cross-domain?

Cross-domain refers to a document or script in a domain try to request resources in another domain, there is a broad cross-domain.

Broad cross-domain:

1.) 资源跳转: A链接、重定向、表单提交
2.) 资源嵌入: <link>、<script><img>、<frame>等dom标签,还有样式中background:url()、@font-face()等文件外链 3.) 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等

In fact, we usually refer to cross-domain is narrow, is limited by the browser requests a class-origin policy scene.

What is the origin policy?
Same Origin Policy / SOP (Same origin policy) is a convention, introduced the browser by Netscape in 1995, it is the core of the browser is also the most basic security feature, if the lack of the same origin policy, the browser is vulnerable to XSS , CSFR attacks. Homologous refers to the so-called "protocol + domain name + port," the same three, even two different domain names point to the same ip address, nor homologs.

Several same-origin policy restrictions following behaviors:

1.) Cookie、LocalStorage 和 IndexDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送

Common cross-domain scenario

URL                                      说明                    是否允许通信
http://www.domain.com/a.js
http://www.domain.com/b.js         同一域名,不同文件或路径           允许
http://www.domain.com/lab/c.js  http://www.domain.com:8000/a.js http://www.domain.com/b.js 同一域名,不同端口 不允许  http://www.domain.com/a.js https://www.domain.com/b.js 同一域名,不同协议 不允许  http://www.domain.com/a.js http://192.168.4.12/b.js 域名和域名对应相同ip 不允许  http://www.domain.com/a.js http://x.domain.com/b.js 主域相同,子域不同 不允许 http://domain.com/c.js  http://www.domain1.com/a.js http://www.domain2.com/b.js 不同域名 不允许

Cross-domain solutions

1, the cross-domain jsonp
2, document.domain + iframe cross-domain
. 3, the location.hash + iframes
. 4, the window.name cross-domain iframes +
5, postMessage cross-domain
6, a cross-domain resource sharing (CORS)
. 7, the agent cross Nginx domain
8, nodejs intermediate cross-domain agent
9, WebSocket cross-domain protocol

First, the cross-domain by jsonp

Usually in order to reduce the load on the web server, we have to separate js, css, img and other static resources to another independent domain name server, and then load the static resources from a different domain in html pages by the respective labels are browser allow, based on this principle, we can create a dynamic script by, and then request a URL with parameters to achieve cross-domain communication.

1.) Native implementation:

 <script>
    var script = document.createElement('script'); script.type = 'text/javascript'; // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数 script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback'; document.head.appendChild(script); // 回调执行函数 function handleCallback(res) { alert(JSON.stringify(res)); } </script>

The server returns the following (i.e., return to perform a global function):

handleCallback({"status": true, "user": "admin"})

2.)jquery ajax:

$.ajax({
    url: 'http://www.domain2.com:8080/login',
    type: 'get',
    dataType: 'jsonp', // 请求方式为jsonp jsonpCallback: "handleCallback", // 自定义回调函数名 data: {} });

3.) vue.js:

this.$http.jsonp('http://www.domain2.com:8080/login', {
    params: {},
    jsonp: 'handleCallback' }).then((res) => { console.log(res); })

Rear node.js Code Example:

var querystring = require('querystring');
var http = require('http'); var server = http.createServer(); server.on('request', function(req, res) { var params = 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 drawback: You can only get one kind of realization request.

Two, document.domain + iframe cross-domain

Only this embodiment same as the primary domain, subdomains of cross-domain scenario.

The principle: two pages are forcibly set document.domain by js-based primary domain, to achieve the same domain.

1.) the parent window :( http://www.domain.com/a.html)

<iframe id="iframe" src="http://child.domain.com/b.html"></iframe> <script> document.domain = 'domain.com'; var user = 'admin'; </script>

2.) child window :( http://child.domain.com/b.html)

<script>
    document.domain = 'domain.com'; // 获取父窗口中变量 alert('get js data from parent ---> ' + window.parent.user); </script>

Three, location.hash + iframe cross-domain

The principle: a and b to be cross-domain communication with each other, an intermediate page is achieved by c. Three pages, the value using the iframe location.hash pass between domains, js direct access to the communication between the same domain.

Specific implementation: A Domain: a.html -> B Domain: b.html -> A domain: c.html, a and b are different domains can communicate via a one-way hash value, b and c are different only a single domain to the communication, but with a same domain c, c so that a page can be accessed by all of the objects parent.parent.

1.)a.html:(http://www.domain1.com/a.html)

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); // 向b.html传hash值 setTimeout(function() { iframe.src = iframe.src + '#user=admin'; }, 1000); // 开放给同域c.html的回调方法 function onCallback(res) { alert('data from c.html ---> ' + res); } </script>

2.)b.html:(http://www.domain2.com/b.html)

<iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); // 监听a.html传来的hash值,再传给c.html window.onhashchange = function () { iframe.src = iframe.src + location.hash; }; </script>

3.)c.html:(http://www.domain1.com/c.html)

<script>
    // 监听b.html传来的hash值
    window.onhashchange = function () { // 再通过操作同域a.html的js回调,将结果传回 window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', '')); }; </script>

Four, window.name + iframe cross-domain

Window.name unique attributes: name value still exists in the different pages (or even different domain) is loaded, and can support very long name value (2MB).

1.)a.html:(http://www.domain1.com/a.html)

var proxy = function(url, callback) {
    var state = 0; var iframe = document.createElement('iframe'); // 加载跨域页面 iframe.src = url; // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name iframe.onload = function() { if (state === 1) { // 第2次onload(同域proxy页)成功后,读取同域window.name中数据 callback(iframe.contentWindow.name); destoryFrame(); } else if (state === 0) { // 第1次onload(跨域页)成功后,切换到同域代理页面 iframe.contentWindow.location = 'http://www.domain1.com/proxy.html'; state = 1; } }; document.body.appendChild(iframe); // 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问) function destoryFrame() { iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } }; // 请求跨域b页面数据 proxy('http://www.domain2.com/b.html', function(data){ alert(data); });

2.) proxy.html :( HTTP: //www.domain1.com/proxy ....
intermediate proxy page, and a.html the same field, the content is blank.

3.)b.html:(http://www.domain2.com/b.html)

<script>
    window.name = 'This is domain2 data!'; </script>

Summary: steering by the local domain by the iframe src attribute of a cross-domain data that is transmitted from the iframe window.name outside the domain to the local domain. This cleverly bypass the cross-domain access to browser limitations, but it is safe to operate.

Five, postMessage cross-domain

postMessage HTML5 XMLHttpRequest Level 2 is in the API, and is one of the few properties window can be cross-domain operations, which can be used to address the following areas:
. A) and a new page of data transmission windows open
b). messages between multiple windows
c.) pages and nested iframe messaging
d.) cross-domain data transfer of the above three scenes

Usage: postMessage (data, origin) method takes two parameters
data: html5 specification supports substantially any type or copy an object, but only some browsers support a string, when it is best to use the JSON.stringify transmission parameters () of the sequence .
origin: protocol, host, port number, may be set to "*" represents any window to pass, if you want to specify the current window and then homologous to "/."

1.)a.html:(http://www.domain1.com/a.html)

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { var data = { name: 'aym' }; // 向domain2传送跨域数据 iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com'); }; // 接受domain2返回数据 window.addEventListener('message', function(e) { alert('data from domain2 ---> ' + e.data); }, false); </script>

2.)b.html:(http://www.domain2.com/b.html)

<script>
    // 接收domain1的数据
    window.addEventListener('message', function(e) { alert('data from domain1 ---> ' + e.data); var data = JSON.parse(e.data); if (data) { data.number = 16; // 处理后再发回domain1 window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com'); } }, false); </script>

Six Cross-Origin Resource Sharing (CORS)

Ordinary cross-domain requests: only the server settings Access-Control-Allow-Origin can be, no need to set the front end, with a cookie request to: the front and rear ends need to be set.

Note that: Due to limitations of the same origin policy, read cookie cookie for cross-domain requests the domain interface, instead of the current page. If you want to achieve cookie written to the current page, you can see below: Seven, nginx reverse proxy settings proxy_cookie_domain and VIII, NodeJs middleware agent cookieDomainRewrite set parameters.

Currently, all browsers support this feature (IE8 +: IE8 / 9 XDomainRequest need to use objects to support CORS)), CORS has become the mainstream of cross-domain solutions.

1, front end:

1.) primeval ajax

// 前端设置是否带cookie
xhr.withCredentials = true;

Sample code:

var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

// 前端设置是否带cookie
xhr.withCredentials = true; 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.)jQuery ajax

$.ajax({
    ...
   xhrFields: {
       withCredentials: true    // 前端设置是否带cookie
   },
   crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie ... });

3.) vue framework

. A) axios provided:

axios.defaults.withCredentials = true

. B) vue-resource settings:

Vue.http.options.credentials = true
2, the server settings:

If the back-end set up, the front console browser does not cross-domain error message appears, on the contrary, shows no set success.

1.) Java background:

/*
 * 导入包:import javax.servlet.http.HttpServletResponse;
 * 接口参数中定义:HttpServletResponse response
 */

// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); 

// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示 response.setHeader("Access-Control-Allow-Credentials", "true"); // 提示OPTIONS预检时,后端需要设置的两个常用自定义头 response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");

2.) Nodejs background Example:

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', // 允许访问的域(协议+域名+端口) /* * 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现), * 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问 */ 'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly的作用是让js无法读取cookie }); res.write(JSON.stringify(postData)); res.end(); }); }); server.listen('8080'); console.log('Server is running at port 8080...');

Seven, nginx proxy cross-domain

1, nginx configuration to solve cross-domain iconfont

Browser cross-domain access js, css, img and other conventional static resources permit same-origin policy, but iconfont font file (eot | otf | ttf | woff | svg) exception, then you can add the following static resource configuration nginx server .

location / {
  add_header Access-Control-Allow-Origin *;
}
2, nginx reverse proxy interfaces across domains

Cross-domain principle: Same Origin Policy is part of the browser's security policy, not the HTTP protocol. HTTP server-side call interface is just using the HTTP protocol, does not perform JS script, no same-origin policy does not exist across a problem.

Realization of ideas: nginx configuration through a proxy (with the same domain domain1, different ports) server springboard machine, reverse proxy access domain2 interfaces, and can modify the way the cookie domain information to facilitate the current domain cookie writing cross-domain login.

nginx specific configuration:

#proxy服务器
server {
    listen       81;
    server_name www.domain1.com; location / { proxy_pass http://www.domain2.com:8080; #反向代理 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://www.domain1.com; #当前端只跨域不带cookie时,可为* add_header Access-Control-Allow-Credentials true; } }

1) front end Code Example:

var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问nginx中的代理服务器 xhr.open('get', 'http://www.domain1.com:81/?user=admin', true); xhr.send();

2.) Nodejs background Example:

var http = require('http');
var server = http.createServer();
var qs = require('querystring'); server.on('request', function(req, res) { var params = qs.parse(req.url.substring(2)); // 向前台写cookie res.writeHead(200, { 'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:脚本无法读取 }); res.write(JSON.stringify(params)); res.end(); }); server.listen('8080'); console.log('Server is running at port 8080...');

Eight, Nodejs middleware proxy cross-domain

Middleware cross-domain node agent, nginx substantially the same principle, is open through a proxy server, to realize the forwarding data, modifying the response header may be a cookie cookieDomainRewrite domain by setting parameters, and writes a cookie of the current domain, to facilitate Interface login authentication.

1, the non-cross-domain frame vue (cross-domain 2)

Use node + express + http-proxy-middleware set up a proxy server.

1) front end Code Example:

var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问http-proxy-middleware代理服务器 xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true); xhr.send();

2.) middleware server:

var express = require('express');
var proxy = require('http-proxy-middleware'); var app = express(); app.use('/', proxy({ // 代理跨域目标接口 target: 'http://www.domain2.com:8080', changeOrigin: true, // 修改响应头信息,实现跨域并允许带cookie onProxyRes: function(proxyRes, req, res) { res.header('Access-Control-Allow-Origin', 'http://www.domain1.com'); res.header('Access-Control-Allow-Credentials', 'true'); }, // 修改响应信息中的cookie域名 cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改 })); app.listen(3000); console.log('Proxy server is listen at port 3000...');

3.) Nodejs background with (f: nginx)

2, vue frame cross-domain (cross-domain 1)

Using node + webpack + webpack-dev-server proxy interface cross-domain. In the development environment, since vue rendering services and interfaces agency services webpack-dev-server are the same, so no cross-domain between pages and proxy interface, no need to set up headers cross-domain information.

webpack.config.js part of the configuration:

module.exports = {
    entry: {},
    module: {},
    ...
    devServer: {
        historyApiFallback: true, proxy: [{ context: '/login', target: 'http://www.domain2.com:8080', // 代理跨域目标接口 changeOrigin: true, secure: false, // 当代理某些https服务报错时用 cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改 }], noInfo: true } }

Nine, WebSocket protocol across domains

HTML5 WebSocket protocol is a new protocol. It implements the browser and the server full-duplex communication, while allowing cross-domain communications, a server push technology well implemented.
Native WebSocket API inconvenient to use, we use Socket.io, it nicely encapsulates webSocket interface that provides a more simple, flexible interface, also does not support webSocket browser provides backwards compatibility.

1) front-end code:

<div>user input:<input type="text"></div> <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script> <script> var socket = io('http://www.domain2.com:8080'); // 连接成功处理 socket.on('connect', function() { // 监听服务端消息 socket.on('message', function(msg) { console.log('data from server: ---> ' + msg); }); // 监听服务端关闭 socket.on('disconnect', function() { console.log('Server socket has closed.'); }); }); document.getElementsByTagName('input')[0].onblur = function() { socket.send(this.value); }; </script>

2.) Nodejs socket background:

var http = require('http');
var socket = require('socket.io'); // 启http服务 var server = http.createServer(function(req, res) { res.writeHead(200, { 'Content-type': 'text/html' }); res.end(); }); server.listen('8080'); console.log('Server is running at port 8080...'); // 监听socket连接 socket.listen(server).on('connection', function(client) { // 接收信息 client.on('message', function(msg) { client.send('hello:' + msg); console.log('data from client: ---> ' + msg); }); // 断开处理 client.on('disconnect', function() { console.log('Client socket has closed.'); });

Guess you like

Origin www.cnblogs.com/danchaofan123/p/12010480.html