Cross-domain is a kind of security policy of browser, but also in front-end development and interview, there are often related problems. Take it out again today, just in time to tidy up and memorize.

Cross domain definition

Cross-domain is a class of request scenarios that are restricted by the browser’s same-origin policy:

  • Cookies, LocalStorage, and IndexDB cannot be read between domains
  • DOM and Js objects are not available between domains
  • AJAX requests cannot be sent between domains

The Same Origin Policy (SOP) is a convention introduced by Netscape into the browser in 1995. It is the core and most basic security function of the browser. Without the Same Origin policy, the browser is vulnerable to XSS and CSFR attacks. Same-origin means that the protocol, domain name, and port are the same. Even if two different domain names point to the same IP address, they are not same-origin.

Note:

  1. If protocols and ports cause cross-domain problems, the Foreground cannot resolve them
  2. Even if different domain names point to the same IP address, it is called cross-domain
  3. The cross-domain request can be sent, and the server can receive the request and return the result normally, but the result is blocked by the browser

The solution

1. JSONP

Jsonp uses

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

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

    // The callback executes the function
    function handleCallback(res) {
        alert(JSON.stringify(res));
    }
 </script>
Copy the code

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

handleCallback({"data": []."user": "admin"})
Copy the code

Features: Advantages of simple compatibility, can be used to solve the problem of cross-domain data access in mainstream browsers. The disadvantage is that support for only GET methods is limited, requires back-end support, and is not secure and may be exposed to XSS attacks.

2. Cross-domain Resource Sharing (CORS)

CORS is a W3C standard, which stands for “Cross-origin Resource Sharing”. It allows browsers to issue XMLHttpRequest requests across source servers, overcoming the limitation that AJAX can only be used in the same source. CORS requires both browser and server support. Currently, all browsers support this function, and Internet Explorer cannot be lower than Internet Explorer 10.

The entire CORS communication process is completed automatically by the browser without user participation. For developers, CORS communication is no different from same-origin AJAX communication, and the code is exactly the same. As soon as the browser discovers that an AJAX request crosses the source, it automatically adds some additional headers, and sometimes an additional request, but the user doesn’t feel it. Therefore, the key to achieve CORS communication is the server. You can enable CORS by setting access-Control-Allow-Origin on the server.

Browsers classify CORS cross-domain requests as simple and non-simple.

If the following conditions are met, it is a simple request. Otherwise, it is a non-simple request:

  1. Use one of the following methods: head, GET, post
  2. Header fields other than Accept, Accept-language, Content-language and Content-Type are not allowed to be set artificially
  3. Content-type values are limited to one of the following: Text /plain, multipart/form-data, Application/X-www-form-urlencoded
  4. None of the XMLHttpRequestUpload objects in the request have any event listeners registered; The XMLHttpRequestUpload object can be accessed using the xmlHttprequest.upload attribute
  5. No ReadableStream object is used in the request

A simple request

location The first field instructions
Request header Origin Automatically added by the browser to indicate the source of the request (protocol + domain + port)
Response headers Access-Control-allow-Origin Control the visibility of the data. If you want the data to be visible to anyone, fill in an “*”
Response headers Access-control-allow-credentials (optional) There is only one optional value to indicate whether cookies are included in the request: true (must be lowercase). If cookies are not included, omit this item instead of filling in false.
Response headers Access-control-expose-headers (optional) In CORS requests, the getResponseHeader() method of the XMLHttpRequest object takes only six basic fields: Cache-control, Content-language, Content-Type, Expires, Last-Modified, Pragma. If you want to get other fields, you must specify access-Control-expose-headers.

Non-simple request

For non-simple requests, the browser first sends a “pre-request OPTIONS”, in which case the server returns a” pre-response “as a response. A pre-request is actually a permission request to the server. The actual request is executed only when the pre-request is successfully returned.

location The first field instructions
Request header Access-Control-Request-Method This content is the type of actual request, which can be simple requests like GET, POST, PUT, DELETE, and so on.
Request header Access-Control-Request-Headers The entry is a comma-separated list of headers used in complex requests.
Response headers Access-Control-allow-Origin As with simple requests, a field must be included.
Response headers Access-Control-allow-methods A comma-separated list of responses to the access-Control-request-method in the pre-request. Although a client may only request one method, the server can still return all allowed methods for the client to cache.
Response headers Access-control-request-headers (Must be included if the pre-request contains access-Control-request-headers) This is a comma-separated list of access-Control-request-headers replies to a pre-request that returns all supported Headers.
Response headers Access-control-allow-credentials (optional) Same as simple requests.
Response headers Access-control-max-age (optional) Specifies the validity period of this precheck request, in seconds.

– As with simple requests, a field must be included.

3. Nginx reverse proxy

Through nginx configure a proxy server (domain name and domain1 the same, different port) as a jumper, reverse proxy access to domain2 interface, and can incidentally modify the cookie domain information, convenient cookie writing in the current domain, to achieve cross-domain login.

Nginx configuration is as follows:

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

    location / {
        proxy_pass   http://www.domain2.com:8080; # Reverse proxyproxy_cookie_domain www.domain2.com www.domain1.com; # change cookie domain name index index.html index.htm; Add_header access-Control-allow-origin HTTP: add_header access-Control-allow-origin HTTP: add_header access-Control-allow-origin HTTP: add_header access-Control-allow-origin HTTP: add_header access-Control-allow-origin//www.domain1.com; # If the current end is cross-domain only without cookies, the value can be *
        add_header Access-Control-Allow-Credentials true; }}Copy the code

Nodejs middleware proxy

The same origin policy is the standard that browsers need to follow, but not if the server is making requests to the server.

The proxy server accepts the client request, forwards the request to the server, and forwards the server response to the client.

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

    // Change the cookie domain name in the response information
    cookieDomainRewrite: 'www.domain1.com'  // The value can be false, indicating no change
}));

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

5. PostMessage across domains

PostMessage is an API in HTML5 XMLHttpRequest Level 2, and is one of the few window properties that can be manipulated across domains. The postMessage method allows limited asynchronous communication between scripts from different sources. Can achieve cross-text file, multi-window, cross-domain messaging. It can be used to solve the following problems:

  1. Data transfer between the page and the new window it opens
  2. Messaging between multiple Windows
  3. Page with nested IFrame message delivery
  4. Cross-domain data transfer for the three scenarios above

window.postMessage(message, targetOrigin, [transfer]);

  • Message: Data to be sent to another window.
  • TargetOrigin: Specifies which Windows can receive message events via the origin property of the window. The value can be a string “*” (for unrestricted) or a URI. If any of the protocol, host address, or port of the target window does not match the value provided by targetOrigin, the message will not be sent. A message will only be sent if all three match.
  • Transfer (Optional) : A string of Transferable objects that are transferred at the same time as Message. Ownership of these objects is transferred to the receiver of the message, and ownership is no longer retained by the sender.

Here’s an example:

// 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: 'abc'
        };
        // Send cross-domain data to domain2
        iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
    };

    // Accept data from domain2
    // MessageEvent has several important attributes: data: the message sent, source: the object of the message sending window, origin: the source of the message sending window (protocol + host + port number)
    window.addEventListener('message'.function(e) {
        alert('data from domain2 ---> ' + e.data);
    }, false);
</script>


// www.domain2.com/b.html
window.onmessage = function(e) {
    console.log(e.data)
    e.source.postMessage('success', e.origin);
 }
Copy the code

6. WebSocket protocol is cross-domain

Websocket is a persistent protocol of HTML5, which realizes the full duplex communication between browser and server, and is also a cross-domain solution. WebSocket and HTTP are both application layer protocols based on TCP. However, WebSocket is a two-way communication protocol. After the connection is established, both the WebSocket server and client can actively send or receive data to each other. At the same time, the WebSocket needs to use HTTP protocol to establish a connection. After the connection is established, the two-way communication between the client and server is independent of HTTP.

Front end:

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

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

    // The listener server is closed
    socket.on('disconnect'.function() { 
        console.log('Server socket has closed.'); 
    });
});

document.getElementsByTagName('input') [0].onblur = function() {
    socket.send(this.value);
};
</script>
Copy the code

Back-end (node) :

var http = require('http');
var socket = require('socket.io');

// Start the 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.'); 
    });
});
Copy the code

7. document.domain + iframe

This mode can be used only when the secondary domain names are the same. For example, a.test.com and b.test.com are used in this mode. Test.com =’test.com’; document.domain =’test.com’;

1.) parent window :(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>
Copy the code

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

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

8. location.hash + iframe

Implementation principle: A wants to communicate with B across domains, which is achieved through the middle page C. Three pages, different fields use iframe location.hash to transfer values, the same fields directly js access to communicate.

A domain: A.html -> B domain: B.html -> A domain: C.HTML, A and B different domain can only hash value one-way communication, B and C are also different domain can only one-way communication, but C and A are the same domain, so C can access all objects on A page through parent. Parent.

1.) A.HTML (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 values to B.html
    setTimeout(function() {
        iframe.src = iframe.src + '#user=admin';
    }, 1000);
    
    // callback methods open to homologous C.HTML
    function onCallback(res) {
        alert('data from c.html ---> ' + res);
    }
</script>
Copy the code

2.) B.HTML (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 hash values from A.html and pass them to C.HTML
    window.onhashchange = function () {
        iframe.src = iframe.src + location.hash;
    };
</script>
Copy the code

3.) C. HTML (www.domain1.com/c.html)

<script>
    // Listen for hash values from B.html
    window.onhashchange = function () {
        // Return the result by manipulating the javascript callback of the same domain A.html
        window.parent.parent.onCallback('hello: ' + location.hash.replace('#user='.' '));
    };
</script>
Copy the code

9. window.name + iframe

Take advantage of the fact that window.name remains the same value even after the page is reloaded (or not). If you create an iframe element on page A, the iframe.onload event will trigger twice. After the first onload(cross-domain proxy page) succeeds, the local proxy page will be switched to. After the second onload(local proxy page) succeeds, the data in local window.name will be read.

1.) A.HTML (www.domain1.com/a.html)

var proxy = function(url, callback) {
    var state = 0;
    var iframe = document.createElement('iframe');

    // Load the cross-domain page
    iframe.src = url;

    // The onload event fires twice, the first time the cross-domain page is loaded and the data is stored in window.name
    iframe.onload = function() {
        if (state === 1) {
            // After the second onload(syndomain proxy page) succeeds, the data in syndomain window.name is read
            callback(iframe.contentWindow.name);
            destoryFrame();

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

    // After the data is retrieved, the iframe is destroyed to free memory; This also ensures security (not accessed by other fields frame JS)
    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);
});
Copy the code

2.) proxy.html :(www.domain1.com/proxy): intermediate proxy page, same domain as a.html, content is empty.

3.) B.HTML (www.domain2.com/b.html)

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

reference

  1. Nine common Front-end cross-domain solutions
  2. Nine Cross-domain Implementation Principles (Full version)
  3. Common Cross-domain solutions at the front end (full)
  4. Cross-domain implementation using iframe and location.hash
  5. Window. name + iframe is implemented across domains
  6. Html5 postMessage addresses cross-domain, cross-window messaging