9 kinds of cross-domain method to solve the problem browser

What is cross-domain?

Cross-domain means the browser can not execute scripts other sites. It is composed of the same origin policy browsers caused a browser security restrictions to JavaScript implementation.

Here to explain, it is not cross-domain browser for user safety reasons, if they did not write the same origin policy browser, no need to consider cross-domain problem. Browser pot, right.

Origin policy restrictions a bit behavior:

Cookie, LocalStorage and can not read IndexDB

JS and DOM objects can not get

Ajax request does not go out

Put it more bluntly, it is that we use ajax to send an asynchronous request at the front end, if the URL address this request to the address bar URL address of the current protocol is different in different domain name, not a port at the same time, have become cross-domain,

Here Insert Picture Description

After the emergence of cross-domain problems, the following error in the console:

Here Insert Picture Description

Below, a few cross-domain chestnuts:

URL Explanation Whether to allow communication
http://www.demo.com/a.js
http://www.demo.com/b.js
http://www.demo.com/lab/c.js
The same domain name, or a different file path allow
http://www.demo.com:8000/a.js
http://www.demo.com/b.js
The same domain name, a different port Not allowed
http://www.demo.com/a.js
https://www.demo.com/b.js
The same domain name, different protocols Not allowed
http://www.demo.com/a.js
http://127.0.0.1/b.js
And domain names correspond to the same ip Not allowed
http://www.demo.com/a.js
http://x.demo.com/b.js
http://demo.com/c.js
Same primary domain, subdomains of Not allowed
http://www.demo1.com/a.js
http://www.demo2.com/b.js
Different domain Not allowed

Cross-domain solutions

  1. By cross-domain jsonp
  2. document.domain + iframe cross-domain
  3. location.hash + iframe
  4. window.name + iframe cross-domain
  5. Cross-domain postMessage
  6. Cross-Origin Resource Sharing (CORS)
  7. nginx proxy cross-domain
  8. nodejs middleware agent across domains
  9. WebSocket protocol across domains

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';

    // 传参并指定回调执行函数为onBack
    script.src = 'http://www.demo2.com:8080/login?user=admin&callback=onBack';
    document.head.appendChild(script);

    // 回调执行函数
    function onBack(res) {
        alert(JSON.stringify(res));
    }
 </script>

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

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

2)jquery ajax:

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

3) vue.js:

this.$http.jsonp('http://www.demo2.com:8080/login', {
    params: {},
    jsonp: 'onBack'
}).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.demo.com/a.html) )

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

Child window :( http://child.demo.com/b.html) )

<script>
    document.domain = 'demo.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.

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.demo1.com/a.html))

<iframe id="iframe" src="http://www.demo2.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.demo2.com/b.html))

<iframe id="iframe" src="http://www.demo1.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.demo1.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.demo1.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.demo1.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.demo2.com/b.html', function(data){
    alert(data);
});

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

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

<script>
    window.name = 'This is demo2 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 aspects:
A) the data page and to open the new window is transmitted
b) a window messaging between
c) and nested iframe page messaging
d) the above three scenarios cross-domain data transfer

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.demo1.com/a.html))

<iframe id="iframe" src="http://www.demo2.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.demo2.com');
    };

    // 接受domain2返回数据
    window.addEventListener('message', function(e) {
        alert('data from demo2 ---> ' + e.data);
    }, false);
</script>

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

<script>
    // 接收domain1的数据
    window.addEventListener('message', function(e) {
        alert('data from demo1 ---> ' + e.data);

        var data = JSON.parse(e.data);
        if (data) {
            data.number = 16;

            // 处理后再发回domain1
            window.parent.postMessage(JSON.stringify(data), 'http://www.demo1.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) native ajax

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

Sample code:

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

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

xhr.open('post', 'http://www.demo2.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 frame
adding the following code ajax assembly vue-resource encapsulated:

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) PHP background:

response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com");  // 若有端口需写全(协议+域名+端口)
response.setHeader("Access-Control-Allow-Credentials", "true");

2) Nodejs Background Example:

ar 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.demo1.com',    // 允许访问的域(协议+域名+端口)
            'Set-Cookie': 'l=a123456;Path=/;Domain=www.demo2.com;HttpOnly'   // HttpOnly:脚本无法读取cookie
        });

        res.write(JSON.stringify(postData));
        res.end();
    });
});

server.listen('8080');
console.log('Server is running at port 8080...');

Seven, 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.demo1.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.demo2.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.demo1.com'  // 可以为false,表示不修改
}));

app.listen(3000);
console.log('Proxy server is listen at port 3000...');

3) Nodejs background

ar 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.demo2.com;HttpOnly'   // HttpOnly:脚本无法读取
    });

    res.write(JSON.stringify(params));
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

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.demo2.com:8080',  // 代理跨域目标接口
            changeOrigin: true,
            secure: false,  // 当代理某些https服务报错时用
            cookieDomainRewrite: 'www.demo1.com'  // 可以为false,表示不修改
        }],
        noInfo: true
    }
}

Eight, 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="./socket.io.js"></script>
<script>
var socket = io('http://www.demo2.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:

// 启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.'); 
    });
});

Nine, 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 (the same domain name demo1, different ports) server springboard machine, reverse proxy access demo2 interfaces, and can modify the way the cookie demo information to facilitate the current domain cookie writing cross-domain login.

nginx specific configuration:

#proxy服务器
server {
    listen       81;
    server_name  www.demo1.com;

    location / {
        proxy_pass   http://www.demo2.com:8080;  #反向代理
        proxy_cookie_demo www.demo2.com www.demo1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.demo1.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.demo1.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.demo2.com;HttpOnly'   // HttpOnly:脚本无法读取
    });

    res.write(JSON.stringify(params));
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

Here Insert Picture Description

Published 124 original articles · won praise 9 · views 20000 +

Guess you like

Origin blog.csdn.net/p445098355/article/details/105220112