Front end cross domain solution (Reprint)

What is cross domain?

Cross domain refers to the document or script under one domain trying to request resources under another domain, where cross domain is generalized.

Generalized 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 links of background:url(), @ font face () and other files in the style
 3.) script request: ajax request initiated by js, cross domain operation of dom and js object, etc

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

What is homology strategy?
The Same origin policy (SOP) is a kind of agreement, which was introduced by Netscape company 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" is the same, even if two different domain names point to the same ip address, they are not homologous.

The homology strategy 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

Common cross domain scenarios

URL                                      Explain                    Allow communication or not
http://www.domain.com/a.js
http://www.domain.com/b.js the same domain name, different files or paths allow
http://www.domain.com/lab/c.js

http://www.domain.com:8000/a.js
http://www.domain.com/b.js same domain name, different ports are not allowed
 
http://www.domain.com/a.js
https://www.domain.com/b.js same domain name, different agreements do not allow
 
http://www.domain.com/a.js
http://192.168.4.12/b.js domain name and domain name corresponding to the same ip are not allowed
 
http://www.domain.com/a.js
http://x.domain.com/b.js the main domain is the same, but the sub domains are different, which 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 JSON
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 agent cross domain
8. nodejs middleware agent cross domain
9. WebSocket protocol cross domain

1, Cross domain through JSON

Generally, in order to reduce the load of web server, we separate static resources such as js, css, img and so on to another server with independent domain name, and then load static resources from different domain names through corresponding tags in html page, which is allowed by browser. Based on this principle, we can create script dynamically 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 for the convenience of executing 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 (when it returns, the global function is executed):

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

2.)jquery ajax:

$.ajax({
    url: 'http://www.domain2.com:8080/login',
    type: 'get',
    dataType: 'jsonp',  // Request mode is JSON
    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); 
})

Code example of backend node.js:

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;

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

The disadvantage of jsonp is that it can only get one request.

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.domain as the basic primary domain through js, and the same domain is realized.

1.) 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.) sub window:( http://child.domain.com/b.html)

<script>
    document.domain = 'domain.com';
    // Get variables in 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 can be realized through middle page c. In the three pages, iframe location.hash is used to pass values between different domains, and js access is directly used to communicate between the same domains.

Specific implementation: a domain: a.html - > b domain: b.html - > a domain: c.html. Different domains of a and B can only communicate in one direction through hash value, and different domains of B and C can only communicate in one direction, but C and a are the same domain, so C can access all objects of a page through 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');

    // Pass hash value to b.html
    setTimeout(function() {
        iframe.src = iframe.src + '#user=admin';
    }, 1000);
    
    // Callback method open to the same domain 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');

    // 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 to the hash value from b.html
    window.onhashchange = function () {
        // Then, the result is returned by operating the js callback of the same domain a.html
        window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
    };
</script>

4, window.name + iframe cross domain

The uniqueness of the window.name attribute: the name value still exists after different pages (or 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 trigger twice. The first time, the cross domain page will be loaded and the data will be saved in window.name
    iframe.onload = function() {
        if (state === 1) {
            // After the second onload (same domain proxy page) succeeds, read the data in the same domain window.name
            callback(iframe.contentWindow.name);
            destoryFrame();

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

    document.body.appendChild(iframe);

    // Destroy the iframe and free the memory after getting the data; this also ensures the security (not accessed by frame js of 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 proxy page is the same domain as a.html, and the content is 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 data is transferred from the foreign domain to the local domain, and the cross domain data is transferred from the foreign domain to the local domain by the window.name of iframe. This skilfully bypasses the browser's cross domain access restrictions, but at the same time it is a safe operation.

5, postMessage cross domain

postMessage is an API in HTML5 XMLHttpRequest Level 2, and one of the few window properties that can be operated 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 of the above three scenarios

Usage: the postMessage(data,origin) method takes two parameters
The data: html5 specification supports any basic type or replicable object, but some browsers only support strings, so it is better to serialize parameters with JSON.stringify().
origin: Protocol + host + port number. It can also be set to "*", which means it can be passed to any window. If you want to specify the same source as the current window, it can be set 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'
        };
        // Transfer cross domain data to domain2
        iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
    };

    // Accept domain2 return data
    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;

            // Process and send back domain1
            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. To bring cookie request: both the front end and the back end need to be set.

It should be noted that due to the restriction of the same source policy, the read cookie is the cookie of the domain where the cross domain request interface is located, rather than the current page. If you want to write the current page cookie, please refer to the following: 7. Setting the proxy cookie domain in nginx reverse proxy and 8. Setting the cookie domainrewrite parameter in NodeJs middleware proxy.

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

// Whether the front-end settings have cookie s
xhr.withCredentials = true;

Example code:

var xhr = new XMLHttpRequest(); // IE8/9 needs to be compatible with window.XDomainRequest

// Whether the front-end settings have cookie s
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    // Whether the front-end settings have cookie s
   },
   crossDomain: true,   // The request header will contain additional information across domains, but will not contain cookie s
    ...
});

3.) vue framework

a.) axios settings:

axios.defaults.withCredentials = true

b. ) Vue resource settings:

Vue.http.options.credentials = true

2. Server settings:

If the backend is set successfully, the cross domain error message will not appear in the front browser console. Otherwise, it means that the backend is not set successfully.

1.) Java background:

/*
 * Import package: import javax.servlet.http.HttpServletResponse;
 * Defined in the interface parameter: HttpServletResponse response
 */

// Domain name that allows cross domain access: if there is a port, write 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 cookie s with authentication on the front end: after this option is enabled, the domain name above 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 back end when prompted for 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 receiving completed
    req.addListener('end', function() {
        postData = qs.parse(postData);

        // Cross domain background settings
        res.writeHead(200, {
            'Access-Control-Allow-Credentials': 'true',     // Back end allows sending cookies
            'Access-Control-Allow-Origin': 'http://www.domain1.com ', / / allowed domains (protocol + domain name + port)
            /* 
             * The cookie set here is still domain2, not domain1, because the backend can't write cookies across domains (nginx reverse proxy can implement it),
             * However, as long as cookie authentication is written once in domain2, subsequent 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'  // HttpOnly is used to make js unable to read cookie s
        });

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

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

7, nginx agent cross domain

1. nginx configuration solves iconfont cross domain

Browser cross domain access to js, css, img and other conventional static resources is allowed by the same origin policy, but iconfont font file (eot|otf|ttf|woff|svg) is the exception. 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 same origin policy is the browser's security policy, not part of the HTTP protocol. The server calls HTTP interface only using HTTP protocol, does not execute JS script, does not need homology policy, and there is no cross problem.

Implementation idea: configure a proxy server (domain name is the same as domain1, port is different) through nginx as a springboard machine, and access the domain2 interface by reverse proxy, and modify the domain information in the cookie by the way, so as 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 agent
        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 restriction of homology. The following cross domain configuration can not be enabled
        add_header Access-Control-Allow-Origin http://www.domain1.com; ා when the current end only cross domains without cookie s, 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 cookie s
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 cookie s to the front desk
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly: script cannot 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 can realize data forwarding by starting a proxy server, or modify the domain name in the cookie in the response header by setting the cookie domain rewrite parameter, so as to write the cookie in the current domain and facilitate the login and authentication of the interface.

1. Cross domain of non vue framework (twice 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 cookie s
xhr.withCredentials = true;

// Visit 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({
    // Agent cross domain target interface
    target: 'http://www.domain2.com:8080',
    changeOrigin: true,

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

    // Modify cookie domain name in response information
    cookieDomainRewrite: 'www.domain1.com'  // It can be false, indicating no modification
}));

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

3.) the same for Nodejs background (VI: nginx)

2. Cross domain of vue framework (once cross domain)

Using node + webback + webback dev server proxy interface to cross domains. In the development environment, since vue rendering service and interface proxy service are the same as web pack dev server, there is no need to set cross domain information between page and proxy interface.

Partial configuration of webpack.config.js:

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,  // Use when some https services report errors
            cookieDomainRewrite: 'www.domain1.com'  // It can be false, indicating no modification
        }],
        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 not very convenient 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 webSocket.

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 processed successfully
socket.on('connect', function() {
    // Listen to server messages
    socket.on('message', function(msg) {
        console.log('data from server: ---> ' + msg); 
    });

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

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

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

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

Original address: Front end common cross domain solutions (all) 

142 original articles published, 54 praised, 90000 visitors+
Private letter follow

Tags: socket JSON Nginx Vue

Posted on Thu, 13 Feb 2020 05:09:59 -0800 by WesPear