First, what is cross-domain

In a broad sense, cross-domain:

1.) Resource jump: A link, redirect, form submission 2.<link>,<script>,<img>,<frame>3.) Script request: AJAX request initiated by JS, cross-domain operation of DOM and JS objects, etcCopy the code

In fact, what we usually mean by cross-domain is narrowly defined, a class of request scenarios that are restricted by the browser’s same-origin policy. This is when the browser cannot execute scripts from other sites. Is a security restriction that browsers impose on JavaScript.

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.

The same origin policy restricts the following behaviors:

  • Cookie, LocalStorage, and IndexDB cannot be read

  • DOM and JS objects cannot be retrieved

  • The Ajax request could not be sent

Pay special attention to two points: first, if protocols and ports cause cross-domain problems, the “foreground” is powerless. Second: in terms of cross-domain problems, domains are only identified by “the head of the URL” without trying to determine whether the same IP address corresponds to two domains or whether two domains are on the same IP address.

The header of the URL refers to window.location.protocol +window.location.host.

2. Common cross-domain scenarios

URL indicates whether communication is allowedhttp://www.domain.com/a.js
http://www.domain.com/b.js Same domain name, different files or paths 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, different ports are not allowed
 
http://www.domain.com/a.js
https://www.domain.com/b.js Same domain name, different protocols do not allow
 
http://www.domain.com/a.js
http://192.168.4.12/b. Js domain names cannot correspond to the same IP address
 
http://www.domain.com/a.js
http://x.domain.com/b.js The primary domain is the same, but the subdomain is different
http://domain.com/c.js
 
http://www.domain1.com/a.js
http://www.domain2.com/b.js different domain names are not allowedCopy the code

3. Common cross-domain solutions



Cross domains via JSONP

In order to reduce the load of the Web server, static resources such as JS, CSS and IMG are separated to another server with an independent domain name, and static resources are loaded from different domain names in the HTML page through corresponding tags, which are allowed by the browser. Based on this principle, we can dynamically create script. Request a reference url to achieve cross-domain communication.

JSONP consists of two parts: callback functions and data.

The callback function is the function that is called to be placed on the current page when the response arrives.

The data is the JSONP data passed into the callback function, which is the argument to the callback function

Jsonp takes advantage of the non-cross-domain nature of the Script tag by appending the callback name to the parameter of the SRC URL. The server then receives the callback name and returns a callback containing the datafunction doSomething(data) {
  // Process data
}
var script = document.createElement("script");
script.src = "http://www.b.com/b.html?callback=doSomething";
document.body.appendChild(script);

// 1. Generate a script tag, append it to the body, and send a request to the server
// 2. DoSomething ({"a", "1"})
// 3. If doSomething(data) is declared, run the doSomething(data) function to get the dataExample front-end with JSONP wrapped in JQ $.ajax({url:'http://192.168.9.5/jsonp_test1.jsp',
             dataType:"jsonp",
             jsonp:"jsonpcallback",
             success:function(data){
                 var $ul = $("<ul></ul>");
                 $.each(data,function(i,v){$("<li/>").text(v["id"] + "" + v["name"]).appendTo($ul)
                 });
                 $("#res").append($ul); }}); Server processing C#
[HttpGet]
        public ActionResult getSurvey_Contact(String contact_id,string survey_id)
        {
            var callback = Request.QueryString["callback"];   // Get the name of the callback
            var contactColl = MyMongoDb.CurrentDB.GetCollection("Survey_Contact");
            if (contact_id == null)
                contact_id = String.Empty;

            var doc = contactColl.FindAs<BsonDocument>(Query.And(Query.EQ("contact_id", contact_id), Query.EQ("Survey_Id", survey_id))).FirstOrDefault();
            if (doc == null)
                doc = new BsonDocument();
            FormateJSON(new[] { doc });
            var answerColl = MyMongoDb.CurrentDB.GetCollection("Survey_Answers");
            var answerDocs = answerColl.FindAs<BsonDocument>(Query.And(Query.EQ("contact_id", contact_id), Query.EQ("survey_id", survey_id))).ToArray();
            FormateJSON(answerDocs);
            doc.Set("answers".new BsonArray(answerDocs));
            var result = new ContentResult();
            result.ContentEncoding = System.Text.Encoding.UTF8;
            result.ContentType = "application/json";
            //Regex reg = new Regex("ObjectId\\(([a-z0-9])\\)");
            if (String.IsNullOrWhiteSpace(callback))
            {
                result.Content = doc.ToJson();
                return result;
            }
            else
            {
                result.Content = callback + "(" + doc.ToJson() + ");";   // The front-end uses the js file format to parse the result, and obtains the data through callback(data)
                returnresult; }}Copy the code
/ / the topicJsonp for jquery or Jsonp for $. Ajax has nothing to do with Ajax, but jquery wraps it around it for easy use. In fact, JSONP takes advantage of the fact that script tags have no cross-domain constraints: It is not restricted by the same origin policy as Ajax requests implemented by XMLHttpRequest objects; It is more compatible, runs in older browsers and does not require XMLHttpRequest or ActiveX support; After the request is complete, the result can be returned by calling callback. Disadvantages of JSONP: It only supports GET requests and does not support other types of HTTP requests such as POST. It only supports cross-domain HTTP requests and does not solve the problem of how to make JavaScript calls between two pages in different domains.Copy the code



Document.domain + iframe addresses cross-domain

This solution applies only to cross-domain scenarios where the primary domain is the same and the subdomains are different.

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

Example 1

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

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

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

Example 2

This approach applies to the same primary domain but different subdomains, such as http://www.a.com and http://b.a.com, if the two domains have a.html and B.html, a.html document.domain ="a.com";
    var iframe = document.createElement("iframe");
    iframe.src = "http://b.a.com/b.html";
    document.body.appendChild(iframe);
    iframe.onload = function() { console.log(iframe.contentWindow....) ; } b.html document.domain ="a.com"; Note: Document.domain needs to be set to its own parent field or higher, and the primary field must be the same.Copy the code




Location. hash + iframe resolve cross-domain

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

2.) b.h 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 a.htmlhashValue, which is passed to C.html window.onhashchange =function () {
        iframe.src = iframe.src + location.hash;
    };
</script>Copy the code

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

<script> // Listen for b.HTML incominghashValue 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='.' '));
    };Copy the code



Window. name + iframe resolve cross-domain

The window.name property sets or returns a string that holds the window name. The window.name attribute is unique in that the name value persists after loading on different pages (and even different domain names) and can support a very long name value (2MB).


var proxy = function(url, callback) {
    var state = 0;
    var iframe = document.createElement('iframe'); // Load the cross-domain page iframe.src = url; // The onload event is triggered twice, the first time the cross-domain page is loaded, and the data is saved in window.name iframe.onload =function() {
        if(state = = = 1) {/ / second onload after the success of the page (with domain proxy), read the same window. The domain name of the callback data (iframe. ContentWindow. Name); destoryFrame(); }else if(state = = = 0) {/ / first onload (cross-domain pages) after the success, switch to sympatric agent iframe page. The 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: (http://www.domain1.com/proxy… Intermediate proxy page, same domain as A.HTML, content is empty.

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

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

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


PostMessage cross-domain

PostMessage is an API in HTML5 XMLHttpRequest Level 2, and is one of the few window properties 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 transfer between multiple Windows c.) message transfer between the page and nested iframe d.) cross-domain data transfer for the three scenarios above

Usage: The postMessage(data, Origin) method takes two arguments data: the HTML5 specification supports any primitive type or replicable object, but some browsers only support strings, so it’s best to serialize the argument with json.stringify (). Origin: protocol + host + port number. The value can also be set to “*”, which indicates that it can be sent to any window. If you want to specify the origin of the current window, set it to “/”.

1.) a.h 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'}; / / to send cross domain data domain2 iframe. ContentWindow. PostMessage (JSON. Stringify (data),'http://www.domain2.com'); }; // Accept domain2 returns data window.adDeventListener ('message'.function(e) {
        alert('data from domain2 ---> ' + e.data);
    }, false);
</script>Copy the code

2.) b.h 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 back again after processing domain1 window. The parent. PostMessage (JSON. Stringify (data),'http://www.domain1.com'); }},false);
</script>Copy the code



Cross-domain Resource Sharing (CORS)

For common cross-domain requests, only access-Control-allow-origin is required on the server. This parameter is not required on the front end. If cookie is required, access-Control-allow-origin is required on both the front and back ends.

Note 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 resides, not the current page. Nginx reverse proxy sets proxy_cookie_domain and cookieDomainRewrite (NodeJs).

Currently, all browsers support this functionality (IE8+ : IE8/9 requires the use of XDomainRequest objects to support CORS), and CORS has become a mainstream cross-domain solution.

1. Front-end Settings:

1.) Native Ajax

// The front end sets whether to bring cookie xhr.withcredentials =true;Copy the code

Sample code:

var xhr = new XMLHttpRequest(); // Ie8/9 requires window.XDomainRequest compatibility // The front end is set to enable cookie 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); }};Copy the code

2.) the jQuery ajax

$.ajax({
    ...
   xhrFields: {
       withCredentials: true// front-end Settings with cookie}, crossDomain:true// Request headers contain additional cross-domain information, but cookies are not included... });Copy the code

3.) The VUE framework adds the following code to the Vue-Resource wrapped Ajax component:

Vue.http.options.credentials = trueCopy the code
2. Server Settings:

If the Settings are successful, the console of the front-end browser will not display cross-domain error messages; otherwise, the Settings are not successful.

1.) Java background:

/ * * import packages: import javax.mail. Servlet. HTTP. HttpServletResponse; * Interface parameters defined in: HttpServletResponse Response */ // Allow cross-domain access domain name: write full port (protocol + domain name + port) if there is no port, do not add the end of the port'/'
response.setHeader("Access-Control-Allow-Origin"."http://www.domain1.com"); // Allow front-end authentication cookie: After this parameter is enabled, the domain name cannot beThe '*', you must specify a specific domain name, otherwise the browser will prompt response.setheader ("Access-Control-Allow-Credentials"."true"); Copy the code

2.) Nodejs

var 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); // set res.writehead (200, {'Access-Control-Allow-Credentials': 'true'// The backend allows sending cookies'Access-Control-Allow-Origin': 'http://www.domain1.com'// Allow access to domain (protocol + domain + port) /* * Set the cookie to domain2, not domain1, because the backend can not write cookies (nginx reverse proxy can be implemented), * but as long as domain2 write cookie authentication once, All subsequent cross-domain interfaces can obtain cookies from domain2, so that all interfaces can cross-domain access */'Set-Cookie': 'l=a123456; Path=/; Domain=www.domain2.com; HttpOnly'// HttpOnly prevents javascript from reading cookies}); res.write(JSON.stringify(postData)); res.end(); }); }); server.listen('8080');
console.log('Server is running at port 8080... ');Copy the code



Nginx agents cross domains

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

1.) Front-end code examples:

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

2.) Nodejs

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



Nodejs middleware proxies cross domains

Node middleware implements cross-domain proxy, which is roughly the same as nginx. It starts a proxy server to forward data. It can also modify the domain name in the cookie in the response header by setting the cookieDomainRewrite parameter to implement cookie writing in the current domain, facilitating interface login authentication.

1. Cross-domain of non-VUE framework (twice cross-domain)

Build a proxy server with Node + Express + HTTP-proxy-middleware.

1.) Front-end code examples:

var xhr = new XMLHttpRequest(); // Front-end switch: whether the browser reads and 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();Copy the code

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 to 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 the cookie domain name in the response message cookieDomainRewrite:'www.domain1.com'/ / can befalse})); app.listen(3000); console.log('Proxy server is listen at port 3000... ');Copy the code

3. Nginx: Nginx

2. Cross-domain of VUE framework (once cross-domain)

Cross domain using node + Webpack + webpack-dev-server agent interface. In the development environment, since the Vue rendering service and the interface proxy service are the same as Webpack-dev-server, there is no need to set headers 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 destination interface changeOrigin:true,
            secure: false// Use cookieDomainRewrite when brokering an error for some HTTPS service:'www.domain1.com'/ / can befalse}], noInfo:true}}Copy the code

The 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.

1.) Front-end code:

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