First, what is cross-domain?
In the front-end world, cross-domain means that the browser allows cross-domain requests to be sent to the server, overcoming the limitation that Ajax can only be used same-origin.
What is the same origin policy?
Netscape introduced the same origin policy 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 are not available
- AJAX requests cannot be sent
2. Common cross-domain scenarios
Nine cross-domain solutions
1. JSONP is cross-domain
Jsonp uses
<script> var script = document.createElement('script'); script.type = 'text/javascript'; // Pass a callback function name to the back end, Convenient back-end returned to carry out the defined in front of the callback function script. The SRC = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback'; document.head.appendChild(script); Function handleCallback(res) {alert(json.stringify (res)); } </script>Copy the code
The server returns the following (executes the global function when it returns) :
handleCallback({"success": true, "user": "admin"})
Copy the code
2) jquery Ajax implementation:
$. Ajax ({url: 'http://www.domain2.com:8080/login', type: "get", dataType: 'the json, / / requests for the json jsonpCallback: "HandleCallback ", // custom callback name data: {}});Copy the code
3) Vue Axios implementation
this.$http = axios;
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
Copy the code
Back-end Node.js code:
var querystring = require('querystring'); var http = require('http'); var server = http.createServer(); server.on('request', function(req, res) { var params = querystring.parse(req.url.split('? ') [1]); var fn = params.callback; // jsonp returns setting 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... ');Copy the code
Disadvantages of JSONP: Only one get request can be sent.
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. Browsers classify CORS cross-domain requests as simple and non-simple. A simple request (1) uses one of the following methods as long as both conditions are met:
- head
- get
- post
(2) The requested Heder is
- Accept
- Accept-Language
- Content-Language
- Content-type: Only three values: Application/X-www-form-urlencoded, multipart/form-data, text/plain
If the two conditions are not met at the same time, it is a non-simple request. Browsers treat these two differently.
A simple request
For simple requests, the browser issues CORS requests directly. Specifically, add an Origin field to the header information.
GET /cors HTTP/1.1 Origin: http://api.bob.com Host: api.alice.com Accept-language: en-us Connection: Keep alive - the user-agent: Mozilla / 5.0...Copy the code
In the header above, the Origin field specifies the source (protocol + domain + port) from which the request came. Based on this value, the server decides whether to approve the request or not.
The response header fields set by CORS requests all start with access-Control – :
Access-control-allow-origin: Mandatory Its value is either the Origin field value at the time of the request or an *, indicating that requests from any domain name are accepted. Access-control-allow-credentials: Optional Its value is a Boolean value indicating whether cookies are allowed or not. By default, cookies are not included in CORS requests. If set to true, the server explicitly approves that cookies can be included in the request and sent to the server. This value can only be set to true if the server does not want the browser to send cookies. Access-control-expose-headers: The getResponseHeader() method of the XMLHttpRequest object returns only six basic fields for an optional CORS request: Cache-control, Content-language, Content-Type, Expires, Last-Modified, Pragma. If you want to get other fields, you must specify access-Control-expose-headers. The above example specifies that getResponseHeader(‘ FooBar ‘) can return the value of the FooBar field.
Non-simple request
Non-simple requests are requests that have special requirements on the server, such as the request method being PUT or DELETE, or the content-Type field being of Type Application/JSON. CORS requests that are not simple requests are preceded by an HTTP query request, called a “preflight” request.
Preview the request
The “precheck” request uses the request method OPTIONS, indicating that the request is being queried. In the request header information, the key field is Origin, indicating which source the request comes from. In addition to the Origin field, the precheck request header contains two special fields.
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0..
Copy the code
1) Access-Control-request-method: This Method is used to list HTTP methods used by browsers for CORS requests. For example, PUT. Access-control-request-headers: Optional This field is a comma-separated string that specifies the additional Header field to be sent by the browser for CORS requests. X-custom-header is used in this example.
Response to precheck request
After receiving the precheck Request, the server checks the Origin, access-Control-request-method, and access-Control-request-headers fields and confirms that cross-source requests are allowed, it can respond. Access-control-allow-origin = access-Control-allow-origin = access-Control-allow-origin = access-Control-allow-origin = access-Control-allow-origin = access-Control-allow-origin = access-Control-allow-origin Mandatory Its value is a comma-delimited string indicating all methods supported by the server for cross-domain requests. Notice that all supported methods are returned, not just the one requested by the browser. This is to avoid multiple “pre-check” requests. Access-control-allow-headers the access-Control-allow-headers field is required if the browser Request includes the access-Control-request-headers field. It is also a comma-separated string indicating all header information fields supported by the server, not limited to those requested by the browser in precheck. 3) Access-Control-allow-credentials: Optional This field has the same meaning as in simple requests. 4) Access-Control-max-age: Optional specifies the validity period of the precheck request, in seconds.
CORS cross-domain example
1) Front end setting: \
- Native ajax:
var xhr = new XMLHttpRequest(); // Ie8/9 requires window.XDomainRequest compatibility // The front end is set to whether the 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
- The jquery ajax:
$.ajax({ ... XhrFields: {withCredentials: true}, crossDomain: true, // Will make the request header contain additional information across domains, but not cookies... }); **2) Server Settings ** : - nodejs codeCopy the code
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', // back end allows sending Cookie 'access-Control-allow-origin ': 'http://www.domain1.com', // allowed domain (protocol + domain + port) /* * Set cookies to domain2 instead of domain1, because the back end can not write cookies across domains (nginx reverse proxy can do this), */ 'set-cookie ': 'l=a123456; */' set-cookie ': 'l=a123456; Path=/; Domain=www.domain2.com; HttpOnly' // HttpOnly prevents javascript from reading cookies}); res.write(JSON.stringify(postData)); res.end(); });Copy the code
});
server.listen(‘8080’); console.log(‘Server is running at port 8080… ‘);
Access-control-allow-origin = access-Control-origin = access-Control-allow-origin = access-Control-allow-origin = access-Control-allow-origin = access-Control-allow-origin And other fields. 1) nginx configuration solution 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.Copy the code
location / { add_header Access-Control-Allow-Origin *; }
2) Nginx reverse proxy interface cross domain cross domain problem: The same origin policy is only a security policy for browsers. The server invokes the HTTP interface only using HTTP protocol, and does not need the same origin policy, so there is no cross-domain problem. Nginx configure a proxy server with the same domain name as domain1, different port) as a jumper, reverse proxy access to domain2 interface, and can incidentally modify the cookie domain information, convenient for the current domain cookie writing, cross-domain access. \ \ Nginx configuration:Copy the code
#proxy 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 cookie domain name index index.html index.htm; # When accessing Nignx with middleware proxy interface such as Webpack-dev-server, there is no browser participation, so there is no source restriction. Add_header access-Control-allow-origin http://www.domain1.com; * add_header access-control-allow-credentials true; * add_header access-control-allow-credentials true; }Copy the code
}
Nodejs middleware proxy is cross-domain [node middleware] (HTTP: / / https://www.zhihu.com/search?q=node%E4%B8%AD%E9%97%B4%E4%BB%B6&search_source=Entity&hybrid_search_source=Entit Y&hybrid _search_extra=%7B%22sourceType% 223A %22article% 222C %22sourceId% 223A81809258 %7D) In both cases, a proxy server can be started to forward data. The cookieDomainRewrite parameter can also be set to modify the domain name in the cookie in the response header to write cookies in the current domain, facilitating interface login authentication. \ \ **1) Use node + Express + HTTP-proxy-middleware to build a proxy server. - Front-end code:Copy the code
var xhr = new XMLHttpRequest();
// Whether the browser reads and writes cookies xhr.withCredentials = true;
/ / access HTTP proxy – middleware proxy server XHR. Open (‘ get ‘and’ www.domain1.com:3000/login?user=… ‘, true); xhr.send();
\ - Middleware server code:Copy the code
var express = require(‘express’); var proxy = require(‘http-proxy-middleware’); var app = express();
App. use(‘/’, proxy({// proxy cross-domain target interface target: ‘www.domain2.com:8080’, changeOrigin: true,
// Modify the response header information to implement 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 information, cookieDomainRewrite: 'www.domain1.com' // Can be false, indicating no modificationCopy the code
}));
app.listen(3000); console.log(‘Proxy server is listen at port 3000… ‘);
**2) Vue framework cross-domain **\ \ node + vue + webpack + webpack-dev-server build project, cross-domain request interface, directly modify webpack.config.js configuration. In the development environment, the Vue rendering service and the interface proxy service are the same as Webpack-dev-server, so there is no cross-domain between the page and the proxy interface. \ \ webpack.config.js part configuration:Copy the code
module.exports = { entry: {}, module: {}, … devServer: { historyApiFallback: true, proxy: [{ context: ‘/login’, target: ‘www.domain2.com:8080’, // The proxy cross-domain target interface changeOrigin: true, secure: false, // The proxy uses cookieDomainRewrite when some HTTPS services report an error: ‘www.domain1.com’ // Can be false to indicate that no modification is made}], noInfo: true}}
Document.domain + iframe cross-domain this scheme is only applicable to cross-domain application 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. \ \ parent window: (1) [http://www.domain.com/a.html] (https://link.zhihu.com/?target=http%3A//www.domain.com/a.html))Copy the code
1) child window :(child.domain.com/a.html)
<script> document.domain = 'domain.com'; Console. log('get js data from parent --> '+ window.parent. User); </script>Copy the code
6. Location. hash + iframe 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.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'); SetTimeout (function() {iframe.src = iframe.src + '#user=admin'; }, 1000); 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'); Onhashchange = function () {ifame. SRC = ifame. SRC + location.hash; }; </script>Copy the code
3) C. HTML (www.domain1.com/c.html)
<script> // Listen for window.onhashchange = function () { The results back to the window. The parent. The parent. OnCallback (' hello, '+ location. Hash. Replace (' # user =', ')); }; </script>Copy the code
7, window.name + iframe cross domain
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). 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; Name ifame. Onload = function() {if (state === 1) {// If (state === 1) {// If (state === 1) {// If (state === 1) { Read with domain window. The name of the callback data (iframe. ContentWindow. Name); destoryFrame(); } else if (state === 0) { Switch to the same domain agent 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; It also ensures safety frame js (not by other domain access) 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 page: (www.domain1.com/proxy.html) intermediate proxy, and a.h HTML with domain, the content is empty. \
3) B.HTML (www.domain2.com/b.html)
<script> window.name = 'This is domain2 data! '; </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.
PostMessage is 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:
- Data transfer between the page and the new window it opens
- Messaging between multiple Windows
- Page with nested IFrame message delivery
- 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 copiable 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.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' }; / / to send cross domain data domain2 iframe. ContentWindow. PostMessage (JSON. Stringify (data), 'http://www.domain2.com'); }; Window. addEventListener('message', function(e) {alert('data from domain2 --> '+ e.data); }, false); </script>Copy the code
2) B.HTML (www.domain2.com/b.html)
Windows. addEventListener('message', function(e) {alert('data from domain1 --> '+ e.ata); 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
9. 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: < 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'); On ('connect', function() {// Listen on the server message socket.on('message', function(msg) { console.log('data from server: ---> ' + msg); }); On ('disconnect', function() {console.log('Server socket has closed.'); }); }); document.getElementsByTagName('input')[0].onblur = function() { socket.send(this.value); }; </script>Copy the code
2) 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 '}); 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... '); Socket.listen (server). On ('connection', function(client) { Function (MSG) {client.send('hello: '+ MSG); console.log('data from client: ---> ' + msg); }); On ('disconnect', function() {console.log(' client socket has closed.'); }); });Copy the code
summary
Jsonp is suitable for loading static resources such as JS, CSS, img, etc. of different domain names. CORS (supports all types of HTTP requests, but not below Internet Explorer 10) is suitable for various cross-domain ajax requests; The principle of Nginx proxy cross-domain and NodeJS middleware cross-domain is similar. They both build a server and request HTTP interface directly on the server side, which is suitable for the front-end project to adjust the back-end interface with the separation of the front and back ends. Document.domain +iframe is suitable for cross-domain requests with the same primary domain name and different subdomain names. PostMessage and WebSocket are new HTML5 features, not very compatible, only applicable to mainstream browsers and IE10+. There are so many cross-domain solutions, there is no best, only the most appropriate, according to the specific use of the scenario to select cross-domain solutions. Hope this blog post can bring you a little help ~~