Common front-end cross-domain solutions (full)
What is cross-domain?
Cross-domain refers to documents or scripts in one domain trying to request resources in another domain, where cross-domain is broad.
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 background:url(), @font-face() and other file external links in the style. 3. script requests: ajax requests initiated by js, cross-domain operations of dom and JS objects, etc.
In fact, what we usually call cross-domain is narrow, and it is a kind of request scenario restricted by browser homology policy.
What is homologous strategy?
Same origin policy (SOP) is a kind of agreement. It was introduced into browser by Netscape in 1995. It is the most core and basic security function of browser. Without the Same origin policy, browser is vulnerable to attacks such as XSS and CSFR. The so-called homology refers to the same protocol + domain name + port, even if two different domain names point to the same ip address, it is not homologous.
Homologous strategies limit the following behaviors:
1.) Cookie, Local Storage and IndexDB are unreadable 2.) DOM and Js objects are not available 3. AJAX requests cannot be sent
Common cross-domain scenarios
URL Explain Is communication 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, different ports are not allowed
http://www.domain.com/a.js
https://www.domain.com/b.js The same domain name, different protocols are not allowed
http://www.domain.com/a.js
http://192.168.4.12/b.js domain name and domain name are not allowed to correspond to the same ip
http://www.domain.com/a.js
http://x.domain.com/b.js has the same primary domain and different subdomains are 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 across domains
6. Cross-domain Resource Sharing (CORS)
7. nginx proxy across domains
8. Noejs middleware proxy across domains
9. WebSocket Protocol Cross-Domain
I. Cross-domain through jsonp
Usually in order to reduce the load of web server, we separate static resources such as js, css, img into another server with independent domain name, and load static resources from different domain names through corresponding tags in html pages, which are allowed by browsers. Based on this principle, we can create script dynamically and request again. A web site with reference implements cross-domain communication.
1. Native realization:
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// Pass parameters and specify the callback execution function as onBack
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);
// Callback Execution Function
function onBack(res) {
alert(JSON.stringify(res));
}
</script>
The server returns as follows (when returned, the global function is executed):
onBack({"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: "onBack", // Custom callback function name
data: {}
});
3.)vue.js:
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'onBack'
}).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...');
The disadvantage of jsonp is that only one request for get can be implemented.
2. document.domain + iframe cross-domain
This scheme is limited to cross-domain application scenarios with the same main domain and different sub-domains.
Implementing Principle: Both pages are forced to set document.domain as the basic main domain through js, which implements the same domain.
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>
2. Subwindows:( 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>
3. location.hash + iframe cross-domain
Principle of implementation: a wants to communicate with b across domains, through the middle page c to achieve. Three pages, the location.hash of iframe is used to transfer values between different domains, and direct js access is 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 one-way through hash value, and different domains of b and c can only communicate one-way, but c and a are the same domain, so c can access all objects of 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 method open to homologous c.html
function onCallback(res) {
alert('data from c.html ---> ' + res);
}
</script>
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>
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 js callback of the same domain a.html
window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
};
</script>
IV. window.name + iframe cross-domain
The unique feature of the window.name attribute is that the name value still exists after loading on different pages (or even different domain names) and can support very long name values (2MB).
1.)a.html: (www.domain1.com/a.html)
var proxy = function(url, callback) {
var state = 0;
var iframe = document.createElement('iframe');
// Loading cross-domain pages
iframe.src = url;
// The onload event triggers two times, the first time the cross-domain page is loaded, and the data is saved in window.name.
iframe.onload = function() {
if (state === 1) {
// After the second onload (the 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) is successful, switch to the same domain proxy page
iframe.contentWindow.location = 'http://www.domain1.com/proxy.html';
state = 1;
}
};
document.body.appendChild(iframe);
// The iframe is destroyed and memory is freed after data is acquired; this also ensures security (not accessed by other domain 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);
});
2.)proxy.html: (www.domain1.com/proxy....
Intermediate proxy page, same domain as a.html, empty content can be.
3.)b.html: (www.domain2.com/b.html)
<script>
window.name = 'This is domain2 data!';
</script>
Summary: By transferring the src attribute of iframe from outer domain to local domain, cross-domain data is transferred from outer domain to local domain by window.name of iframe. This skillfully bypasses the browser's cross-domain access restrictions, but at the same time it is a secure operation.
5. postMessage across domains
PosMessage is an API in HTML5 XMLHttpRequest Level 2 and is one of the few window s attributes that can be operated across domains. It can be used to solve the following problems:
a.) Data transfer between pages and new windows they open
b.) Message passing between multiple windows
c.) Pages 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 HTML 5 specification supports any basic type or duplicated object, but some browsers only support strings, so JSON.stringify() is the best way to serialize parameters.
origin: The protocol + host + port number can also be set to "*", which means that it can be passed to any window. If it is to be specified to be homologous to the current window, it can be set to "/".
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');
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: (www.domain2.com/b.html)
<script>
// Receiving domain1 data
window.addEventListener('message', function(e) {
alert('data from domain1 ---> ' + e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
// After processing, it is sent back to domain1
window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
}
}, false);
</script>
6. Cross-domain Resource Sharing (CORS)
Ordinary cross-domain requests: Only the server can set Access-Control-Allow-Origin, and the front-end does not need to be set.
With cookie requests: Both front and back ends need to set fields, and note that the cookie brought is the cookie in the domain where the cross-domain request interface is located, not the current page.
At present, all browsers support this function (IE8+: IE8/9 needs XDomainRequest object to support CORS). CORS has become the mainstream cross-domain solution.
1. Front-end settings:
1. native ajax
// Front-end settings with or without cookie s
xhr.withCredentials = true;
Sample code:
var xhr = new XMLHttpRequest(); // IE8/9 requires window.XDomainRequest compatibility
// Front-end settings with or without 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 // Front-end settings with or without cookie s
},
crossDomain: true, // Make the request header contain additional information across domains, but not cookie s
...
});
3. The Vue framework adds the following code to the ajax component encapsulated by vue-resource:
Vue.http.options.credentials = true
2. Server settings:
If the back-end is set up successfully, the front-end browser console will not appear cross-domain error message, on the contrary, it means that it has not been set up successfully.
1.) Java background:
/*
* Import package: import javax.servlet.http.HttpServletResponse;
* Definition in interface parameters: HttpServletResponse response
*/
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); // Write all ports (protocol + domain name + port)
response.setHeader("Access-Control-Allow-Credentials", "true");
2. Background example of Nodejs:
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', // Backend allows sending Cookie s
'Access-Control-Allow-Origin': 'http://www.domain1.com', // Accessible Domains (Protocol + Domain Name + Port)
'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly: The script cannot read cookie s
});
res.write(JSON.stringify(postData));
res.end();
});
});
server.listen('8080');
console.log('Server is running at port 8080...');
7. nginx proxy across domains
1. nginx configuration resolves iconfont cross-domain
The browser's cross-domain access to conventional static resources such as js, css, img is licensed by the Same-Origin policy, with the exception of iconfont font file (eot|otf|ttf|woff|svg), in which case the following configuration can be added to the static resource server of nginx.
location / {
add_header Access-Control-Allow-Origin *;
}
2. Cross-domain nginx reverse proxy interface
Cross-domain Principle: Homology policy is the browser's security policy, not part of the HTTP protocol. The HTTP interface invoked on the server side only uses HTTP protocol, does not execute JS scripts, does not need the same source policy, and there is no cross-over problem.
The idea of implementation is to configure a proxy server (domain name is the same as domain1, port is different) as a springboard machine through nginx, access domain2 interface by reverse proxy, and modify domain information in cookie conveniently to facilitate cookie writing in the current domain, and realize 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; #Modifying domain names in cookie s
index index.html index.htm;
# When accessing nignx with middleware proxy interfaces such as webpack-dev-server, there is no browser involved, 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 is only cross-domain 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. Background examples of 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 s to the front desk
res.writeHead(200, {
'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly: The 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 proxy across domains
node middleware implements cross-domain proxy. The principle is roughly the same as nginx. It implements data forwarding by starting a proxy server.
1. Cross-domain of non-vue frameworks (2 cross-domain)
Using 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;
// 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 response header information to achieve cross-domain and allow cookie s
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' // Can be false, indicating no modification
}));
app.listen(3000);
console.log('Proxy server is listen at port 3000...');
3. Nodejs backstage is the same (6: nginx)
2. Cross-domain of vue framework (1 cross-domain)
Using 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 headers cross-domain information between pages and proxy interface.
The webpack.config.js section is configured as follows:
module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true,
proxy: [{
context: '/login',
target: 'http://www.domain2.com:8080', // Proxy Cross-Domain Target Interface
changeOrigin: true,
cookieDomainRewrite: 'www.domain1.com' // Can be false, indicating no modification
}],
noInfo: true
}
}
9. Cross-domain WebSocket Protocol
WebSocket protocol is a new protocol for 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 encapsulates the webSocket interface well, provides a simpler and more flexible interface, and also provides downward compatibility for browsers that do not support webSocket.
1. Front-end code:
<div>user input: <input type="text"></div>
<script src="./socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');
// Connection successful processing
socket.on('connect', function() {
// Listening for Server Messages
socket.on('message', function(msg) {
console.log('data from server: ---> ' + msg);
});
// Monitor Server Closed
socket.on('disconnect', function() {
console.log(