The article lists the solution and the corresponding demo, refused to say the concept, not confused.
What happens when you cross domains?
- Agreement is different
- Domain name is different
- Different ports
Cross-domain solutions
1. Cross-domain request between different subdomains in the same primary domain -document. domain+iframe
In the same origin, the parent page can access the global variables, DOM tree, etc. of the iframe directly via iframe.contentWindow, and the iframe can do the same with the parent/top page.
domain.html
<body>
<iframe id="ifr" src="http://b.tblog.com:3004/domain2.html"></iframe>
<script>
document.domain = 'tblog.com';
function aa(str) {
console.log(str);
}
window.onload = function () {
document.querySelector('#ifr').contentWindow.bb('aaa');
}
</script>
Copy the code
domain2.html
<body>
2222222222
<script>
document.domain = 'tblog.com';
function bb(str) {
console.log(str);
}
parent.aa('bbb');
</script>
</body>
Copy the code
Complete demo
2. Completely different source – postMessage
Html5 new API, support IE8+.
otherWindow.postMessage(message, targetOrigin, [transfer]);
Copy the code
- OtherWindow A reference to another window, such as the contentWindow property of iframe, the window object returned by executing window.open, or the window.frames named or numeric index.
- Message is the data to be sent to the other window
- TargetOrigin specifies which Windows receive message events via the origin property of the window. The value can be a string.”“(for unrestricted) or a URI. If you know exactly which window the message should be sent to, always provide a targetOrigin with an exact value instead of. Not providing an exact target will result in data being leaked to any malicious site interested in the data.
- Transfer is optionally a string of Transferable objects that are passed 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.
The attributes of the message passed in are:
- Data An object passed from another window.
- Origin The origin of the message sender window when calling postMessage. The string is a combination of protocol, ://, domain name, and: port number
- Source A reference to the window object from which the message was sent; You can use this to establish two-way communication between two Windows with different origins
Below index.html and index2.html communicate with index.html
<body>
<input type="text" placeholder="http://b.tblog.com:3004/index2.html">
<iframe src="Http://192.168.101.5: 3004 / index2. HTML" frameborder="0"></iframe>
<script>
const input = document.querySelector('input');
input.addEventListener('input'.function () {
window.frames[0].postMessage(this.value, The '*');
// window.frames[0].postMessage(this.value, 'http://192.168.101.5');
// window.frames[0].postMessage(this.value, 'http://192.168.101.5:3004'); }); // Receive message window.adDeventListener ('message'.function (e) {
input.value = e.data;
console.log('Parent window', e.data);
console.log('Parent window', e.source);
console.log('Parent window', e.origin);
});
</script>
</body>
Copy the code
index2.html
<body> child window <input id="input" type="text" placeholder="http://a.tblog.com:3004/index.html">
<script>
const input = document.querySelector('#input');
input.addEventListener('input'.function () {
window.parent.postMessage(this.value, The '*'); }); // Receive message window.adDeventListener ('message'.function (e) {
input.value = e.data;
console.log('Child window', e.data);
console.log('Child window', e.source);
console.log('Child window', e.origin);
});
</script>
</body>
Copy the code
Complete demo
3. Completely different source – location.hash+iframe
The idea is to use location.hash to pass values. Changing the hash does not result in a page refresh, so the hash value can be used to pass data, although the data capacity is limited. For example: suppose a.tblog.com: 3004 and 192.168.101.5:3004 / index2. HTML communication principle: A.tblog.com: 3004 in the index. The HTML to the iframe will 192.168.101.5:3004 / index2. HTML page introduction, In 192.168.101.5:3004 / index2. New iframe, inserted into HTML pages, and the introduction of the iframe, a.tblog.com: 3004 homologous, Can will 192.168.101.5:3004 / index2. HTML hash data into a.tblog.com: the hash value of 3004 pages. parent.parent.location.hash = self.location.hash.substring(1); a.tblog.com:3004/index.html
<script>
var ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.src = 'http://192.168.101.5:3004/ index2. Html# paramdo';
document.body.appendChild(ifr);
function checkHash() {
try {
var data = location.hash ? location.hash.substring(1) : ' ';
if (console.log) {
console.log('Now the data is ' + data);
}
} catch (e) { };
}
setInterval(checkHash, 2000);
</script>
Copy the code
192.168.101.5:3004 / index2. HTML
<body> <script> // Simulate a simple argument handling operation switch (location.hash) {case '#paramdo':
callBack();
break;
case '#paramset': / /doSomething......break;
}
function callBack() {
try {
parent.location.hash = 'somedata';
} catch (e) {
var ifrproxy = document.createElement('iframe');
ifrproxy.style.display = 'none';
ifrproxy.src = 'http://a.tblog.com:3004/index3.html#somedata'; // Note that the file is in"a.com"Domain of the document. The body. The appendChild (ifrproxy); } } </script> </body>Copy the code
a.tblog.com:3004/index3.html
<body> <script> // Because parent. Parent belongs to the same domain as itself, So you can change the location. The hash value of the parent. The parent. The location. The hash = self. The location. The hash. The substring (1); </script> </body>Copy the code
Complete demo
4. Window. name + iframe cross domains
Window.name Gets/sets the name of the window. The name of the window is mainly used to set targets for hyperlinks and forms. Windows do not need to have names. The window.name property sets or returns a string containing the name of the window. The name value persists after loading from page to page or from field to field, and can store a very long name(2MB). Scenario 1 – Same source A.HTML
<body>
<script type="text/javascript">
const iframe = document.createElement('iframe');
iframe.src = 'http://a.tblog.com:3004/b.html';
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.onload = function () {
console.log(iframe.contentWindow.name)
};
</script>
</body>
Copy the code
b.html
<body>
<script>
window.name = 'Child page data';
</script>
</body>
Copy the code
Scenario 2 – Different sources take advantage of the window.name feature in iframe that survives loading on different pages or domains. A.tblog.com: 3004 / a.h added through the iframe in the HTML 192.168.0.103:3004 / b.h HTML (data page, specify the window. The name of the value), to monitor the load of the iframe, Change iframe SRC with a.tblog.3004 / A.html homology proxy page A.tblog.3004 / C.HTML (empty page). a.tblog.com:3004/a.html
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
let state = 0;
iframe.onload = function () {
console.log('iframe.onload', state, iframe.contentWindow);
if (state === 1) {
const data = JSON.parse(iframe.contentWindow.name);
console.log(data, state);
iframe.contentWindow.document.write(' ');
iframe.contentWindow.close();
document.body.removeChild(iframe);
} else if (state === 0) {
state = 1;
console.log('data', window.name)
iframe.contentWindow.location = 'http://a.tblog.com:3004/c.html'; }}; iframe.src ='http://192.168.0.103:3004/b.html';
document.body.appendChild(iframe);
Copy the code
Complete demo
5. Cross-domain json
The json principle:
- The first is cross-domain implementation using the SRC attribute of the Script tag.
- The client registered callback method name, carries on the URL, such as’ http://127.0.0.1:8080/getNews? callback=getData’
- The server generates a json response, puts the JSON in the callback function it just received, and generates a getData(JSON).
- The client browser inserts the script tag into the DOM, parses the script tag, and executes getData(json).Because the SRC attribute of the script tag is used, only the GET method is supported Client code
<body>
<button class="get">get data</button>
<script>
const btn = document.querySelector('.get');
btn.addEventListener('click'.function () {
const script = document.createElement('script');
script.setAttribute('src'.'http://127.0.0.1:8080/getNews? callback=getData');
document.head.appendChild(script);
document.head.removeChild(script);
})
function getData(news) {
console.log(news)
}
</script>
</body>
Copy the code
Server code
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');
http.createServer(function(req, res){
const pathObj = url.parse(req.url, true);
switch(pathObj.pathname){
case '/getNews':
const news = [{id: 678}];
res.setHeader('Content-type'.'text/json; charset=utf-8');
if(pathObj.query.callback){
res.end(pathObj.query.callback + '(' + JSON.stringify(news) + ') ');
}else {
res.end(JSON.stringify(news));
}
break;
default:
res.writeHead(404, 'not found');
}
}).listen(8080);
Copy the code
Complete demo
6. CORS across domains
Principle Cross-Domain resource sharing (CORS) is a mechanism that uses additional HTTP headers to tell browsers to allow Web applications running on one Origin (domain) to access specified resources from different source servers. The cross-domain resource sharing (CORS) mechanism allows Web application servers to control cross-domain access and secure cross-domain data transfer. When is CORS required
- The cross-domain HTTP request initiated by XMLHttpRequest or Fetch mentioned earlier.
- Web fonts (cross-domain font resources are used in CSS via @font-face), so Web sites can publish TrueType font resources and only allow authorized sites to make cross-site calls.
- WebGL map
- Draw Images/video Images to canvas using drawImage
- Style sheets (using CSSOM)
Cross-domain resource Sharing standards add a new set of HTTP header fields that allow servers to declare which source sites have access to which resources through browsers. Allows the server to declare which source sites have access to which resources through the browser. For requests other than GET, the browser must first issue a preflight request using the OPTIONS method to know whether the server will allow the cross-domain request. The actual HTTP request is made only after the server confirms that it is allowed. The whole process is completed automatically by the browser, and the server will add some additional header information. Therefore, the key to achieve CORS communication is the server. As long as the server implements the CORS interface, cross-source communication is possible. Simple requests Certain requests do not trigger CORS precheck requests. This article refers to such a request as a “simple request,” noting that the term is not part of the Fetch (where CORS is defined) specification. It is a simple request as long as the following two conditions are met:
(1) The request method is one of the following three methods: HEAD GET POST (2) HTTP header does not exceed the following fields: Accept accept-language content-language last-event-id content-type: Application/X-www-form-urlencoded, multipart/form-data, text/plain are limited to three valuesCopy the code
Additional fields in the request response result:
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
Copy the code
- Access-Control-Allow-Origin
Access-Control-Allow-Origin: <origin> | *
; This field is required. The value is either the Origin field value at the time of the request, or an *, indicating that requests are accepted for any domain, and the response header field can cross domains once in a while - Access-Control-Allow-Credentials
Access-Control-Allow-Credentials: true
; When the browser’s credentials are set to true, this response header indicates whether the browser is allowed to read the contents of the response. Credentials can be cookies, Authorization headers, or TLS Client certificates.
Access – Control – Allow – Credentials with XMLHttpRequest head work. WithCredentials or Fetch the Request of the API () constructor Credentials in the option. The Credentials must be configured on both the front and back ends (the Access-Control-allow-credentials header and XHR or Fetch Request) to enable the CORS request with the Credentials to succeed. If the withCredentials are false, the server agrees to send cookies and the browser does not send them, or if the server asks for cookies, the browser does not process them.
Note that access-Control-allow-Origin cannot be set to an asterisk if cookies are to be sent, and must specify an explicit domain name consistent with the requested web page. At the same time, cookies still follow the same origin policy, only the Cookie set with the server domain name will be uploaded, cookies of other domain names will not be uploaded, and (cross-source) document. Cookie in the original web page code can not read cookies under the server domain name.
// Allow the credentials: access-control-allow-credentials:true// Use XHR with credentials: var XHR = new XMLHttpRequest(); xhr.open('GET'.'http://example.com/'.true);
xhr.withCredentials = true;
xhr.send(null);
// 使用带credentials的 Fetch :
fetch(url, {
credentials: 'include'
})
Copy the code
- For cross-domain Access, the getResponseHeader() method of the XMLHttpRequest object gets only the most basic response Headers, Cache-control, Content-language, Content-Type, Expires, Last-Modified, Pragma. You must specify access-Control-expose-headers. The above example specifies that getResponseHeader(‘FooBar’) can return the value of the FooBar field.
The code is as follows:
<! -- Server --> http.createserver (function(req, res){
const pathObj = url.parse(req.url, true);
switch(pathObj.pathname){
case '/user':
const news = [{id: 678}];
res.setHeader('Content-type'.'text/json; charset=utf-8');
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
// res.setHeader('Access-Control-Allow-Origin'.The '*'); // If you need a cookie, you must res.setheader ('Access-Control-Allow-Credentials'.true);
res.end(JSON.stringify(news));
break;
default:
res.writeHead(404, 'not found');
}
}).listen(8080, (err) => {
if(! err) { console.log('8080 started '); }}); <! -- client --> <body> <script> const XHR = new XMLHttpRequest(); xhr.open('GET'.'http://localhost:8080/user'.true); // The credentials such as cookies must be xhr.withcredentials =true;
xhr.onreadystatechange = (e) => {
console.log('onreadystatechange', e)
}
xhr.send();
</script>
</body>
Copy the code
Complete demo
Non-simple request For non-simple CORS requests, an HTTP query request is added before formal communication, called a preflight request. To know if the server will allow the actual request. The use of precheck requests prevents cross-domain requests from having unexpected impacts on the server’s user data. Precheck requests should be sent first when any of the following conditions are met:
- Any of the following HTTP methods are used:
- put
- delete
- connect
- OPTIONS
- trace
- patch
- Other header fields other than the set of cORS security header fields are set artificially. The set is:
- Accept
- Accept-Language
- Content-Language
- Content-Type
- DPR
- Downlink
- Save-data
- Viewport-Width
- Width
- The value of the content-type is not one of the following:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- The XMLHttpRequestUpload object in the request registers any number of event listeners.
- A ReadableStream object is used in the request.
Here is an HTTP request that needs to perform a precheck request:
<body>
<script>
const invocation = new XMLHttpRequest();
const url = 'http://localhost:8080/user';
const body = JSON.stringify({ name: 'toringo' });
function callOtherDomain() {
if (invocation) {
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER'.'pingpong');
invocation.setRequestHeader('Content-Type'.'application/json');
invocation.onreadystatechange = (e) => {
console.log('onreadystatechange', e) }; invocation.send(body); } } callOtherDomain(); </script> </body> <! -- Server --> http.createserver (function(req, res){
const pathObj = url.parse(req.url, true);
switch(pathObj.pathname){
case '/user':
const news = {id: 678};
res.setHeader('Content-type'.'text/json; charset=utf-8');
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
// res.setHeader('Access-Control-Allow-Origin'.The '*'); // If you need a cookie, you must res.setheader ('Access-Control-Allow-Credentials'.true);
res.end(JSON.stringify(news));
break;
default:
res.writeHead(404, 'not found');
}
}).listen(8080, (err) => {
if(! err) { console.log('8080 started '); }});Copy the code
Browser request result:
cors2.html:1 Access to XMLHttpRequest at 'http://localhost:8080/user' from origin 'http://127.0.0.1:3004' has been blocked by CORS policy: Request header field x-pingother is not allowed by Access-Control-Allow-Headers in preflight response.
Copy the code
Access-Control-Request-Method: POST; // This field is required to list the HTTP methods used by the browser for CORS requests. Access-Control-Request-Headers: Content-Type, X-PINGOTHER; Tell the server that the actual request will carry two custom request header fields: X-Pingother and Content-Type. The server then decides whether the actual request is allowed.Copy the code
To successfully respond to the above example, the server needs to agree:
http.createServer(function(req, res){
const pathObj = url.parse(req.url, true);
switch(pathObj.pathname){
case '/user':
const news = {id: 678};
res.setHeader('Content-type'.'text/json; charset=utf-8');
res.setHeader('Access-Control-Allow-Origin', req.headers.origin); // Add res.setheader ('Access-Control-Allow-Methods'.'POST, GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers'.'X-PINGOTHER, Content-Type');
res.setHeader('Access-Control-Max-Age', 86400);
res.end(JSON.stringify(news));
break;
default:
res.writeHead(404, 'not found');
}
}).listen(8080, (err) => {
if(! err) { console.log('8080 started '); }});Copy the code
New fields in the service segment:
Access-Control-Allow-Origin: req.headers.origin Access-Control-Allow-Methods: POST, GET, OPTIONS // indicates that the server allows clients to use the POST, GET, and OPTIONS methods to initiate requests. This field is similar to the HTTP/1.1 Allow: Response Header, but is used only in scenarios where access control is required. This is to avoid multiple times"Preview"The request. Access-Control-Allow-Headers: X-pingother, Content-type // 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 that the server supports all header fields, not limited to the browser's"Preview"The requested field in. Access-control-max-age: 86400 // Indicates that the response has a valid time of 86400 seconds, or 24 hours. The browser does not have to make another precheck request for the same request during the valid time. Note that the browser itself maintains a maximum validity time, and if the value of this header field exceeds the maximum validity time, it will not take effect.Copy the code
7. Nodejs proxy is cross-domain
Node middleware implements cross-domain proxy by using 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 write the cookie in the current domain and facilitate interface login authentication. Principle: There is no cross-domain restriction on data requests between servers (same-origin policy is browser behavior), so the request is first broached to the proxy server, and the proxy server internally requests the real server to get the result and then ends the connection.
<! -- Services --> http.createserver (function(req, res){
const pathObj = url.parse(req.url, true);
console.log('server', pathObj.pathname)
switch(pathObj.pathname){
case '/user':
const news = {id: 678};
res.end(JSON.stringify(news));
break;
default:
res.setHeader('Content-type'.'text/json; charset=utf-8');
res.end('Unknown error');
}
}).listen(4000, (err) => {
if(! err) { console.log('4000 started '); }}); <! -- Proxy --> http.createserver (function(req, res){
const pathObj = url.parse(req.url, true);
switch(pathObj.pathname){
case '/user':
res.setHeader('Content-type'.'text/json; charset=utf-8');
res.writeHead(200, {
'Access-Control-Allow-Origin': The '*'.'Access-Control-Allow-Methods': 'POST, GET, OPTIONS'.'Access-Control-Allow-Headers': 'X-PINGOTHER, Content-Type'}); console.log('proxy', req.method, pathObj.pathname); // Request server const proxyRequest = http.request({host:'127.0.0.1',
port: 4000,
url: '/',
path: pathObj.pathname,
method: req.method,
headers: req.headers
}, (proxyRes) => {
let body = ' ';
proxyRes.on('data', (chunk) => {
body += chunk;
});
proxyRes.on('end', () => {
console.log('Response data' + body );
res.end(body);
})
}).end();
break;
default:
res.writeHead(404, 'not found');
res.end(body);
break;
}
}).listen(8080, (err) => {
if(! err) { console.log('8080 started '); }}); <! -- Client index.html --> <body> <script> const Invocation = new XMLHttpRequest(); const url ='http://localhost:8080/user';
const body = JSON.stringify({ name: 'toringo' });
function callOtherDomain() {
if (invocation) {
invocation.open('POST', url, true);
// invocation.setRequestHeader('X-PINGOTHER'.'pingpong');
invocation.setRequestHeader('Content-Type'.'application/json');
invocation.onreadystatechange = (e) => {
console.log('onreadystatechange', e)
};
invocation.send(body);
}
}
callOtherDomain();
</script>
</body>
Copy the code
Note: Server and browser data interactions also need to follow the same origin policy
— Constantly updated —
Tips: Blog code address. ~ github
WeChat
Refer to the article developer.mozilla.org/zh-CN/docs/… Vinc. Top / 2017/02/09 /… www.ruanyifeng.com/blog/2016/0… Segmentfault.com/a/119000000… Developer.mozilla.org/zh-CN/docs/… Developer.mozilla.org/zh-CN/docs/… www.ruanyifeng.com/blog/2016/0…