preface

First of all, what is cross-domain? Simply understand that because of the restriction of JavaScript same-origin policy, JS under the domain name of a.com cannot operate the object under the domain name of b.com or c.a.com



What is the same origin policy?

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.

1, the json

JSONP is JSON with Padding or parametric JSON.

JSONP implements cross-domain requests simply by dynamically creating <script> tags and then using <script> SRC to retrieve data across domains without being constrained by the same origin policy.

JSONP consists of two parts: the callback function and the data callback function is the function that should be called in the page when the response arrives. The name of the callback function is usually specified in the request. The data is the JSON data passed into the callback function

Create the

var script = document.createElement("script");
script.src = "https://api.douban.com/v2/book/search?q=javascript&count=1&callback=handleResponse"
document.body.insertBefore(script, document.body.firstChild);
Copy the code

On the page, the returned JSON is passed as an argument to the callback function, which we use to manipulate the data.

functionHandleResponse (response){// Code to operate on response data}Copy the code

2, postMessage

PostMessage is an API introduced by HTML5. The postMessage() method allows scripts from different sources to communicate effectively in an asynchronous manner, enabling cross-text document, multi-window, and cross-domain messaging. It is mainly used for inter-window data communication, which makes it an effective solution for cross-domain communication.

Sending data:

otherWindow.postMessage(message, targetOrigin, [transfer]);
Copy the code

otherWindow

A reference to a window, such as the contentWindow property of iframe, the window object returned by executing window.open, or the named or numerically indexed window.frames.

message

Data to be sent to the other Windows, it will be structured clone algorithm [!] (https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm) serialization. This means you can safely pass data objects to the target window without having to serialize them yourself.

targetOrigin

The origin attribute of a window is used to specify which Windows can receive message events. Only the corresponding Windows under the origin attribute can receive messages after being specified. If the wildcard character is set to “*”, the message can be sent to any window, but this is not recommended for security reasons. If you want to send to a window of the same origin as the current window, set it to “/”.

Transfer | optional attribute

Transferable is a list of **Transferable** objects that are passed along with Message. Ownership of these objects is transferred to the receiver of the message, and no ownership is retained by the sender.

Receive data: Listens for message events

window.addEventListener("message", receiveMessage, false);function receiveMessage(event) {
     var origin= event.origin;
     console.log(event);
}Copy the code

The screenshot of the event object is as follows:



The four properties of the Event object

  • Data: a message object sent from another window;
  • Type: indicates the type of message to be sent.
  • Source: the window object from which the message is sent;
  • Origin: refers to the source of the window that sent the message

3. Cross-domain Resource Sharing (CORS)

CORS requires both browser and backend support. Internet Explorer 8 and 9 need to be implemented through XDomainRequest.

The browser will automatically carry out CORS communication, the key to achieve CORS communication is the back end. As long as the backend implements CORS, cross-domain is achieved.

To enable CORS, set access-Control-allow-Origin on the server. This attribute indicates which domain names can access resources. If a wildcard is set, all websites can access resources.

Although setting up CORS has nothing to do with the front end, solving cross-domain problems in this way can result in two cases of simple and complex requests being sent.

(1) Simple request

As long as the following two conditions are met, it is a simple request

Condition 1: Use one of the following methods:

  • GET
  • HEAD
  • POST

Condition 2: The value of the content-Type is limited to one of the following:

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

None of the XMLHttpRequestUpload objects in the request have any event listeners registered; The XMLHttpRequestUpload object can be accessed using the xmlHttprequest.upload attribute.

(2) Non-simple request

A request that does not meet these criteria is definitely a complex request. The CORS request of complex request will add an HTTP query request, called “precheck” request, before formal communication. This request is the option method, through which to know whether the server allows cross-domain request.

When a PUT request is sent to the background, it is a complex request and the background needs to perform the following configuration:

// Which method is allowed to access me res.setheader ('Access-Control-Allow-Methods'.'PUT'// Precheck the survival time res.setheader ('Access-Control-Max-Age', 6) // OPTIONS request no processingif (req.method === 'OPTIONS') {res.end()} // Define what is returned in the background app.put('/getData'.function(req, res) {
  console.log(req.headers)
  res.end('I don't love you')})Copy the code

Let’s look at an example of a complete complex request and look at the CORS request-related fields

// index.html
let xhr = new XMLHttpRequest()
document.cookie = 'name=xiamen'// Cookies cannot cross domain xhr.withcredentials =true// Whether the front end is set with cookie xhr.open('PUT'.'http://localhost:4000/getData'.true)
xhr.setRequestHeader('name'.'xiamen')
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    if((XHR. Status > = 200 && XHR. Status < 300) | | XHR. Status = = = 304) {the console. The log (XHR. Response) / / get the response headers, Access-control-expose-headers console.log(xhr.getresponseHeader ('name'))
    }
  }
}
xhr.send()Copy the code

//server1.js
let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
Copy the code

4. Node middleware proxy

In the process of front-end website development, the network request points to the interface provided by NodeJS, the NodeJS server then initiates the request to point to the cross-domain server, and then returns to the front-end page successively, so as to complete the cross-domain access, basically meeting the cross-domain access problem

The front-end code

var xhr = new XMLHttpRequest(); // Whether the browser reads and writes cookies xhr.withCredentials =true; // Access http-proxy-middleware proxy server xhr.open('get'.'http://www.127.0.0.1:3000/login?user=admin'.true);
xhr.send();Copy the code

express+ http-proxy-middleware

var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();

app.use('/', proxy({// proxy interface target:'http://www.127.0.0.1:8080',
    changeOrigin: true// Modify the response header to cross-domain and allow cookie onProxyRes:function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin'.'http://www.127.0.0.1');
        res.header('Access-Control-Allow-Credentials'.'true'); }, // Modify the cookie domain name in the response message cookieDomainRewrite:'www.127.0.0.1'/ / can befalse})); app.listen(3000);Copy the code

Koa+Koa2-cors

var Koa = require('koa');
var cors = require('koa2-cors');

var app = new Koa();
app.use(cors({
  origin: function(ctx) {
    if (ctx.url === '/') {
      return false;
    }
    return The '*';
  },
  exposeHeaders: ['WWW-Authenticate'.'Server-Authorization'],
  maxAge: 5,
  credentials: true,
  allowMethods: ['GET'.'POST'.'DELETE'],
  allowHeaders: ['Content-Type'.'Authorization'.'Accept']})); app.listen(3000);Copy the code

5. WebSocket protocol is cross-domain

WebSocket Protocol is a new protocol for HTML5. It implements 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 encapsulates the WebSocket interface well, provides a simpler, flexible interface, and provides backward compatibility for browsers that do not support WebSocket.

The front end

The < div > user input: < inputtype="text"></div>
<script src="./socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080'); // Process socket.on('connect'.function() {// Listen for server message socket.on()'message'.function(msg) {
        console.log('data from server: ---> '+ msg); }); // The listener closes the socket.on('disconnect'.function() { 
        console.log('Server socket has closed.'); 
    });
});

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

Nodejs socket background

var http = require('http');
var socket = require('socket.io'); 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 to socket connections socket.listen(server).on('connection'.function(client) {// Receive the message 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

Window. Name + iframe

The window.name attribute is unique in that the name value persists across different pages (and even different domain names) and supports very long name values (2MB).

Where A.HTML and B.HTML are sympatric, both are http://localhost:3000; And c. HTML is http://localhost:4000

 // a.html(http://localhost:3000/b.html)
  <iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe>
  <script>
    let first = true// The onload event fires twice, the first time the cross-domain page is loaded and the data is stored in window.namefunction load() {
      if(first){// After the first onload(cross-domain page) succeeds, switch to the same-domain proxy pagelet iframe = document.getElementById('iframe');
        iframe.src = 'http://localhost:3000/b.html';
        first = false;
      }else{/ / second onload (sympatric b.h HTML pages) after the success, read the same domain window. The name of the data to the console. The log (iframe. ContentWindow. Name); } } </script>Copy the code

B.html is an intermediate proxy page, in the same domain as A.HTML, with empty content.

 // c.html(http://localhost:4000/c.html)
     // c.html(http://localhost:4000/c.html)
  <script>
    window.name = 'I want to cross domains.'  
  </script>
Copy the code

The SRC attribute of iframe is used to transfer the cross-domain data from the outfield to the local region. In this case, the window.name of iframe is used to transfer the cross-domain data from the outfield to the local region. This is a neat way to circumvent the browser’s cross-domain access restrictions, but it’s also a secure operation.

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.

// a.html

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe'); / / to the b.h HTMLhashvaluesetTimeout(function() {
        iframe.src = iframe.src + '#user=admin'; }, 1000); // callback methods open to homologous C.HTMLfunction onCallback(res) {
        alert('data from c.html ---> ' + res);
    }
</script>

Copy the code
// b.html<iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe'); // listen for a.htmlhashValue, which is passed to C.html window.onhashchange =function () {
        iframe.src = iframe.src + location.hash;
    };
</script>Copy the code
// c.html <scripthashValue window. Onhashchange =function() {/ / and by operating with domain a.h HTML js callback, the results back to the window. The parent, the parent. OnCallback ('hello: ' + location.hash.replace('#user='.' '));
    };
</script>Copy the code

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. Just add document.domain =’test.com’ to the page to indicate that the secondary domain is the same.

Implementation principle: two pages through JS forced document.domain as the base of the primary domain, to achieve the same domain.

Let’s look at an example: page a.zf1.cn:3000/a.html gets the value of a in page b.zf1.cn:3000/b.html

// a.html
<body>
 helloa
  <iframe src="http://b.zf1.cn:3000/b.html" frameborder="0" onload="load()" id="frame"></iframe>
  <script>
    document.domain = 'zf1.cn'
    function load() {
      console.log(frame.contentWindow.a);
    }
  </script>
</body>Copy the code

// b.html
<body>
   hellob
   <script>
     document.domain = 'zf1.cn'
     var a = 100;
   </script>
</body>
Copy the code

Nginx proxy cross domain

1, nginx configuration to resolve iconfont cross-domain

Browser cross-domain access js, CSS, and img conventional static resources are the same-origin policy permission, but iconfont font file (eot | otf | the vera.ttf | woff | SVG) exception, at this time in nginx server to add the following configuration static resources.

location / {
  add_header Access-Control-Allow-Origin *;
}Copy the code
2. Nginx reverse proxy interfaces cross domains

Cross-domain principle: The same Origin policy is a security policy of the browser, not a part of the HTTP protocol. The server invokes the HTTP interface only using THE HTTP protocol, and does not execute JS scripts. There is no need for the same origin policy, so there is no crossing problem.

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 in the domain information, convenient for the current domain cookie writing, cross-domain login.

Nginx configuration:

# 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; # change the domain name in cookie
        index  index.html index.htm;

        # When accessing Nignx using middleware proxy interfaces such as Webpack-dev-server, there is no browser participation, so there is no source restriction, the following cross-domain configuration can not be enabled
        add_header Access-Control-Allow-Origin http://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

The front-end code

var xhr = new XMLHttpRequest(); // Front-end switch: whether the browser reads and writes cookies xhr.withCredentials =true; // Access the proxy server xhr.open('get'.'http://www.domain1.com:81/?user=admin'.true);
xhr.send();Copy the code

Nodejs background

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

conclusion

Nginx is recommended to solve cross-domain problems. It has the following advantages:

  • Excellent compatibility, can be used in all browsers
  • Low cost, no additional server configuration, no front-end code modifications
  • Saves server performance
  • It can carry sessions without additional authentication information such as cookies

reference

Cross-domain resource sharing (CORS

window.postMessage

Cross domain issues of front-end access are resolved through nginx reverse proxy

Front end cross-domain summary