Front end common cross domain solutions (all)

Time:2022-1-12

What is cross domain?

Cross domain means that a document or script in one domain attempts to request resources in another domain. Cross domain is generalized here.

Broad cross domain:

1.) resource jump: a link, redirection, form submission
2.) resource embedding: < link >, < script >, < img >, < frame > and other DOM tags, as well as the external chain of files such as background: URL (), @ font face () in the style
3.) script request: Ajax request initiated by JS, cross domain operation of DOM and JS objects, etc

In fact, what we usually call cross domain is a narrow sense, which is a kind of request scenario limited by browser homology policy.

What is homology strategy?
The same origin policy (SOP) is an agreement. The browser was introduced by Netscape in 1995. It is the most core and basic security function of the browser. Without the same origin policy, the browser is vulnerable to XSS, CSFR and other attacks. The so-called homology means that “protocol + domain name + port” are the same. Even if two different domain names point to the same IP address, they are not homologous.

The same origin policy limits the following behaviors:

1.) cookies, localstorage and indexdb cannot be read
2.) Dom and JS objects cannot be obtained
3.) Ajax request cannot be sent

Antecedents and consequences of cross domain requests

The main reason why browsers restrict cross domain requests is security issues, such as CSRF attacks. However, since it is not secure, why do we have to cross domain requests? The reason is that sometimes in order to facilitate server management and reduce server pressure, the company will put different resources on different servers, so there are many sub domains. At this time, for example, if the HTML resources of sub domain a want to access the image resources of sub domain B, there will be cross domain requests.

Common cross domain scenarios

The URL indicates whether communication is allowed
http://www.domain.com/a.js
http://www.domain.com/b.js The same domain name, different files or paths are allowed
http://www.domain.com/lab/c.js

http://www.domain.com:8000/a.js
http://www.domain.com/b.js The same domain name is not allowed on different ports

http://www.domain.com/a.js
https://www.domain.com/b.js The same domain name is not allowed in different protocols

http://www.domain.com/a.js
http://192.168.4.12/b.js Domain name and domain name correspond to the same IP, which is not allowed

http://www.domain.com/a.js
http://x.domain.com/b.js The primary domain is the same, but the sub domains are different. It is not allowed
http://domain.com/c.js

http://www.domain1.com/a.js
http://www.domain2.com/b.js Different domain names are not allowed

Cross domain solutions

1. Cross domain through jsonp
2、 document. Domain + iframe cross domain
3、 location.hash + iframe
4、 window. Name + iframe cross domain
5. PostMessage cross domain
6. Cross domain resource sharing (CORS)
7. Nginx proxy cross domain
8. Nodejs middleware agent cross domain
9. Websocket protocol cross domain

1、 Cross domain through jsonp

Generally, in order to reduce the load of the web server, we separate the static resources such as JS, CSS and img to another server with independent domain name, and then load the static resources from different domain names through the corresponding tags in the HTML page, which is allowed by the browser. Based on this principle, we can dynamically create scripts and request a web address with parameters to realize cross domain communication.

1.) native implementation:

<script>
    var script = document.createElement('script');
    script.type = 'text/javascript';

    //Pass a callback function name to the back end to facilitate the execution of the callback function defined in the front end when the back end returns
    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
    document.head.appendChild(script);

    //Callback execution function
    function handleCallback(res) {
        alert(JSON.stringify(res));
    }
 </script>

The server returns the following (the global function is executed when it returns):

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

2.)jquery ajax:

$.ajax({
    url: 'http://www.domain2.com:8080/login',
    type: 'get',
    Datatype: 'jsonp', // the request method is jsonp
    Jsonpcallback: "handlecallback", // custom callback function name
    data: {}
});

3.)vue.js:

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

Backend 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 return settings
    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...');

Disadvantages of jsonp: only one request can be implemented for get.

2、 Document Domain + iframe cross domain

This scheme is only applicable to cross domain application scenarios with the same primary domain and different sub domains.

Implementation principle: both pages are forced to set document. XML through JS Domain as the basic primary domain, the same domain is realized.

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

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

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

<script>
    document.domain = 'domain.com';
    //Gets the variable in the parent window
    alert('get js data from parent ---> ' + window.parent.user);
</script>

3、 Location Hash + iframe cross domain

Implementation principle: if a wants to communicate with B across domains, it is realized through the middle page C. Three pages, using iframe location between different domains Hash value transfer, direct JS access between the same fields to communicate.

Specific implementation: domain a: a.html – > domain B: b.html – > domain a: c.html. Different domains of a and B can only communicate one way through hash value, and different domains of B and C can only communicate one way, but C and a are in the same domain, so C can communicate through parent Parent accesses all objects on the a page.

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

<iframe src="https://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    //Pass hash value to b.html
    setTimeout(function() {
        iframe.src = iframe.src + '#user=admin';
    }, 1000);

    //Callback methods open to c.html in the same domain
    function onCallback(res) {
        alert('data from c.html ---> ' + res);
    }
</script>

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

<iframe src="https://www.domain1.com/c.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    //Listen for the hash value from a.html and then pass it to c.html
    window.onhashchange = function () {
        iframe.src = iframe.src + location.hash;
    };
</script>

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

<script>
    //Listen for the hash value from b.html
    window.onhashchange = function () {
        //Then, the result is returned by operating the JS callback of a.html in the same domain
        window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
    };
</script>

4、 Window Name + iframe cross domain

window. The uniqueness of the name attribute: the name value still exists after different pages (even different domain names) are loaded, and can support a 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');

    //Load cross domain pages
    iframe.src = url;

    //The onload event will be triggered twice. The cross domain page will be loaded for the first time and the data will be saved in the window name
    iframe.onload = function() {
        if (state === 1) {
            //After the second onload (same domain proxy page) succeeds, read the same domain window Data in name
            callback(iframe.contentWindow.name);
            destoryFrame();

        } else if (state === 0) {
            //After the first onload (cross domain page) is successful, switch to the same domain proxy page
            iframe.contentWindow.location = 'http://www.domain1.com/proxy.html';
            state = 1;
        }
    };

    document.body.appendChild(iframe);

    //After obtaining the data, destroy the iframe to free the memory; This also ensures security (not accessed by frame JS in other domains)
    function destoryFrame() {
        iframe.contentWindow.document.write('');
        iframe.contentWindow.close();
        document.body.removeChild(iframe);
    }
};

//Request cross domain B page data
proxy('http://www.domain2.com/b.html', function(data){
    alert(data);
});

2.)proxy.html:(http://www.domain1.com/proxy….)
The intermediate agent page has the same domain as a.html, and the content can be empty.

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

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

Summary: through the SRC attribute of iframe, the external domain is transferred to the local domain, and the cross domain data is transferred from the window of iframe Name is passed from the foreign domain to the local region. This skilfully bypasses the browser’s cross domain access restrictions, but at the same time, it is a secure operation.

5、 PostMessage cross domain

PostMessage is an API in HTML5 XMLHttpRequest Level 2 and one of the few window attributes that can operate across domains. It can be used to solve the following problems:
a. ) data transfer between the page and the new window it opens
b. ) message passing between multiple windows
c. ) page and nested iframe messaging
d. ) cross domain data transfer in the above three scenarios

Usage: the PostMessage (data, origin) method accepts two parameters
Data: the HTML5 specification supports any basic type or replicable object, but some browsers only support strings, so it’s best to use JSON when passing parameters Stringify() serialization.
Origin: Protocol + host + port number, which can also be set to “*”, indicating that it can be passed to any window. If you want to specify the same source as the current window, set it to “/.

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

<iframe src="https://www.domain2.com/b.html" style="display:none;"></iframe>
<script>       
    var iframe = document.getElementById('iframe');
    iframe.onload = function() {
        var data = {
            name: 'aym'
        };
        //Transfer cross domain data to domain2
        iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
    };

    //Accept the data returned by domain2
    window.addEventListener('message', function(e) {
        alert('data from domain2 ---> ' + e.data);
    }, false);
</script>

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

<script>
    //Receive data from domain1
    window.addEventListener('message', function(e) {
        alert('data from domain1 ---> ' + e.data);

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

            //Send it back to domain1 after processing
            window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
        }
    }, false);
</script>

6、 Cross domain resource sharing (CORS)

Common cross domain request: only the server can set access control allow origin, and the front end does not need to be set. If you want to bring a cookie request: both the front and back ends need to be set.

It should be noted that due to the restriction of the same origin policy, the cookie read is the cookie of the domain where the cross domain request interface is located, not the current page. If you want to write the current page cookie, please refer to the following: VII. Setting proxy in nginx reverse proxy_ cookie_ Domain and VIII. Setting of cookiedomainrewrite parameter in nodejs middleware agent.

At present, all browsers support this function (IE8 +: IE8 / 9 needs to use xdomainrequest object to support CORS), and CORS has become a mainstream cross domain solution.

1. Front end settings:

1.) native Ajax

//Front end setting whether to bring cookies
xhr.withCredentials = true;

Example code:

var xhr = new XMLHttpRequest(); //  IE8 / 9 needs window Xdomainrequest compatible

//Front end setting whether to bring cookies
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 // front end setting whether to bring cookies
   },
   Crossdomain: true, // the request header will contain additional information across domains, but will not contain cookies
    ...
});

3.) Vue frame

a. ) Axios settings:

axios.defaults.withCredentials = true

b. ) Vue resource settings:

Vue.http.options.credentials = true
2. Server settings:

If the back-end settings are successful, the front-end browser console will not display cross domain error messages. On the contrary, it indicates that the settings are not successful.

1.) Java background:

/*
 *Import package: import javax servlet. http. HttpServletResponse;
 *Interface parameters: httpservletresponse
 */

//Domain name that allows cross domain access: if there is a port, write it all (protocol + domain name + port). If there is no port, do not add '/' at the end
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); 

//Allow authentication cookies on the front end: after this option is enabled, the above domain name cannot be '*', you must specify a specific domain name, otherwise the browser will prompt
response.setHeader("Access-Control-Allow-Credentials", "true"); 

//Two common custom headers that need to be set at the backend during options pre check
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 = '';

    //Data block receiving
    req.addListener('data', function(chunk) {
        postData += chunk;
    });

    //Data received
    req.addListener('end', function() {
        postData = qs.parse(postData);

        //Cross domain background settings
        res.writeHead(200, {
            'access control allow credentials':'true', // the backend allows sending cookies
            'Access-Control-Allow-Origin': ' http://www.domain1.com ', // allowed domains (protocol + domain name + port)
            /* 
             *The cookie set here is still domain 2, not domain 1, because the backend cannot write cookies across domains (nginx reverse proxy can be implemented),
             *However, as long as the cookie authentication is written once in domain2, the following cross domain interfaces can obtain cookies from domain2, so that all interfaces can be accessed across domains
             */
            'Set-Cookie': 'l=a123456; Path=/; Domain=www.domain2. com; Httponly '// the purpose of httponly is to make JS unable to read cookies
        });

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

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

7、 Nginx proxy cross domain

1. Nginx configuration solves iconfont cross domain

The browser’s cross domain access to JS, CSS, IMG and other conventional static resources is permitted by the same origin policy, with the exception of iconfont font file (eot|otf|ttf|woff|svg). At this time, the following configuration can be added to the static resource server of nginx.

location / {
  add_header Access-Control-Allow-Origin *;
}
2. Nginx reverse proxy interface Cross Domain

Cross domain principle: the homology policy is the security policy of the browser, not a part of the HTTP protocol. When the server calls the HTTP interface, it only uses the HTTP protocol, does not execute JS scripts, does not need the homology strategy, and there is no crossing problem.

Implementation idea: configure a proxy server (the domain name is the same as domain1 and the port is different) through nginx as a springboard machine. The reverse proxy accesses the domain2 interface, and can modify the domain information in the cookie to facilitate the writing of the current domain cookie and realize cross domain login.

Specific configuration of nginx:

#Proxy server
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_ pass    http://www.domain2.com:8080 # Reverse proxy
        proxy_ cookie_ domain www.domain2. com www.domain1. com; # Modify the domain name in the cookie
        index  index.html index.htm;

        #When using middleware proxy interfaces such as webpack dev server to access nignx, there is no browser participation at this time, so there is no homology restriction. The following cross domain configuration can not be enabled
        add_ header Access-Control-Allow-Origin  http://www.domain1.com # When the current end only spans domains without cookies, it can be*
        add_header Access-Control-Allow-Credentials true;
    }
}

1.) front end code example:

var xhr = new XMLHttpRequest();

//Front end switch: whether the browser reads or writes cookies
xhr.withCredentials = true;

//Accessing the proxy server in 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));

    //Write cookies to the front desk
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456; Path=/; Domain=www.domain2. com; Httponly '// httponly: script cannot be read
    });

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

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

8、 Nodejs middleware agent cross domain

Node middleware implements cross domain proxy. The principle is roughly the same as nginx. It realizes data forwarding by starting a proxy server. It can also modify the domain name in the cookie in the response header by setting the cookie domainrewrite parameter to write the cookie in the current domain to facilitate interface login authentication.

1. Cross domain of non Vue framework (2 times cross domain)

Use node + Express + HTTP proxy middleware to build a proxy server.

1.) front end code example:

var xhr = new XMLHttpRequest();

//Front end switch: whether the browser reads or writes cookies
xhr.withCredentials = true;

//Access HTTP proxy middleware proxy server
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({
    //Proxy cross domain target interface
    target: 'http://www.domain2.com:8080',
    changeOrigin: true,

    //Modify the response header information to achieve cross domain and allow cookies
    onProxyRes: function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
        res.header('Access-Control-Allow-Credentials', 'true');
    },

    //Modify the cookie domain name in the response information
    cookieDomainRewrite: 'www.domain1. Com '// can be false, indicating that it is not modified
}));

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

3.) nodejs background is the same as (VI: nginx)

2. Cross domain of Vue framework (1 time cross domain)

Use node + webpack + webpack dev server proxy interface to cross domain. In the development environment, because Vue rendering service and interface proxy service are the same as webpack dev server, there is no need to set header cross domain information between the page and proxy interface.

webpack. config. JS part configuration:

module.exports = {
    entry: {},
    module: {},
    ...
    devServer: {
        historyApiFallback: true,
        proxy: [{
            context: '/login',
            target: ' http://www.domain2.com:8080 ', // proxy cross domain target interface
            changeOrigin: true,
            Secure: false, // used when the proxy reports an error on some HTTPS services
            cookieDomainRewrite: 'www.domain1. Com '// can be false, indicating that it is not modified
        }],
        noInfo: true
    }
}

9、 Websocket protocol cross domain

Websocket protocol is a new protocol of HTML5. It realizes full duplex communication between browser and server, and allows cross domain communication. It is a good implementation of server push technology.
The native websocket API is inconvenient to use. We use socket IO, which well encapsulates the websocket interface, provides a simpler and more flexible interface, and provides downward compatibility for browsers that do not support WebSockets.

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

//Connection successfully processed
socket.on('connect', function() {
    //Listen for server messages
    socket.on('message', function(msg) {
        console.log('data from server: ---> ' + msg); 
    });

    //Monitor server shutdown
    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');

//Enable HTTP service
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...');

//Listen for socket connections
socket.listen(server).on('connection', function(client) {
    //Receive information
    client.on('message', function(msg) {
        client.send('hello:' + msg);
        console.log('data from client: ---> ' + msg);
    });

    //Disconnect processing
    client.on('disconnect', function() {
        console.log('Client socket has closed.'); 
    });
});