This issue has a cliche, why interviewers like to ask this question, because you can develop a lot of knowledge, such as the same-origin policy, what are the solution to cross domain, what are the strengths and weaknesses of the various, such as the following, the author will moments, hope to be able to let you conquer this a series of problems, let the interviewer shine at the moment, as soon as possible to get the right offer.

What is cross-domain?

The same origin policy is an important security policy implemented by the browser manufacturer. MDN introduces the same origin policy for the browser.

The same origin policy is an important security policy that restricts how an Origin document or the scripts it loads can interact with resources from another source. It can help block malicious documents and reduce the number of vectors that can be attacked.

Without the same origin policy, browsers are vulnerable to XSS and CSRF attacks.

Definition of homology:

A source is a source only when the protocol, domain name, and port are the same. As shown in the figure, the shaded area must be the same. Conversely, if one of the three is inconsistent, cross-domain will occur.Here are a few scenarios:Note that:

  1. By default,httpThe protocol can omit ports80.httpsThe protocol can omit ports443, e.g.http://www.example.com:80andhttp://www.example.comIt’s homologous
  2. If protocols and ports are causing cross-domain problems, there is nothing the “foreground” can do
  3. In cross-domain problems, it is only identified by the “head of the URL”, not by the corresponding domain nameIPThe address, as long asURLIf the header (protocol, domain name, and port) is consistent, it belongs to the same source

Can the server receive cross-domain requests?

It’s easy to get confused here. Since it’s cross-domain, the request sent should be cut down by the browser and never reach the server, but it’s not.Cross-domain requests can be made normally, and the server can receive the request and return the result correctly, but the browser intercepts itTo illustrate:

Always keep in mind that same-origin policies only exist on the browser side, and there are no cross-domain problems on the server, even with tools like Postman

What are the ways to solve cross-domain problems?

Here is a list of 10 cross-domain solutions, which are described below

(1) the CORS

Although the browser will block cross-domain request data returned by the server by default, there is a way for the browser to turn this block off, and that is to use CORS. CORS is a W3C standard, which stands for “Cross-domain Resource Sharing”. It allows browsers to issue XMLHttpRequest requests to cross-domain servers to overcome the same-origin limitation.

CORSBoth the browser and the server need to support this function. Currently, all browsers support this function, but there are version restrictions: CORSThe essence of the implementation is to automatically add some additional header information to the browser request. The wholeCORSThe communication process is automatically completed by the browser without user participation. For developers,CORSCommunication with homologyAjaxThere is no difference in communication, the code is exactly the same and, to the user, imperceptive.

Therefore, the key to CORS communication is the server. As long as the server implements the CORS interface, cross-source communication is possible.

How does the server implement CORS, as mentioned earlier, essentially adds headers to the request. The server will set different headers according to whether the request is a simple request or a non-simple request. What is a simple request and a non-simple request? Here you can see Ruan Yifeng’s cross-domain resource sharing CORS explanation. The most critical of these is setting the response header:

Access-Control-Allow-Origin: <origin> | *
Copy the code

The value of the origin parameter specifies the outfield URL that is allowed for the resource, as configured in Node:

res.setHeader('Access-Control-Allow-Origin', 'http://www.abc.com')
Copy the code

Thus requests from http://www.abc.com will be allowed to cross domains, and if set to *, requests from any domain will be allowed:

res.setHeader('Access-Control-Allow-Origin', '*')
Copy the code

If you are using Node + Express to develop the server, you are recommended to use the NPM package: CORS.

Cors const cors = require('cors') const app = express() / / use the app. Use (cors ())Copy the code

Using third-party packages makes it easier to automatically set headers based on simple or non-simple requests, enabling cross-domain communication.

(2) the json

Like CORS, JSONP is a common way of addressing cross-domains, but there is a drawback that JSONP only supports GET requests, while CORS supports all types of HTTP requests. But it has better browser compatibility and supports older browsers.

We all know that link and script tags support cross-domain communication by themselves, for example:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial - scale = 1.0 "> < link rel =" stylesheet" Href = "https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.1/css/bootstrap.css" > < title > Document < / title > < / head > The < body > < / body > < script SRC = "https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js" > < / script > < script SRC = "https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.1/js/bootstrap.js" > < / script > < / HTML >Copy the code

The basic idea of JSONP is that a web page requests data from the server by adding a

First, the page inserts a

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial-scale =1.0"> <title>Document</title> </head> <body> <script> function addScriptTag(SRC) {var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } window.onload = function () { addScriptTag('http://www.abc.com?callback=getData'); } function getData(data) { console.log('data: ' + data); }; </script> </body> </html>Copy the code

The code above makes a request to the server http://www.abc.com by dynamically adding

When the server receives the request, it returns the data as an argument to the callback function, such as when using Node:

const http = require('http') const server = http.createServer((req, Res) => {if (req.query && req.query.callback) {// Const data = {id: 1, name: 'jx ', age: 18 } const str = req.query.callback + '(' + JSON.stringify(data) + ')' res.end(str) } }) server.listen(80, () => { console.log('server running at http://www.abc.com') })Copy the code

The script requested by the

(3) Node agent

NodeProxy solutions across domains are also commonly used at the framework level. becauseThe same-origin policyIt only exists on the browser side, and the server is not restricted, so it can be passed on the server sideThe agentGet the response data and return it to the browser, with a picture:

3.1 Implemented in Node
//server1 ---http://www.abc.com const http = require('http') const server = http.createServer((req, Req. setHeader({' access-Control-allow-origin ': {' access-Control-allow-origin ': '*', 'Access-Control-Allow-Methods': '*', 'Access-Control-Allow-Headers': Request ({host: 'http://www.example.com', path: '/getDaata', port: 80, method: req.method, headers: req.headers }, request => { let body = ''; request.on('data', (chunk) => { body += chunk; }).on('end', () => { res.end(body); }) }) }) server.listen(80, () => { console.log('server running at http://www.abc.com') })Copy the code
//server2 ---http://www.example.com const http = require('http') const server = http.createServer((req, Res) => {const data = {id: 1, name: 'j3 ', age: 18} // charset=utf-8') res.end(JSON.stringify(data)) }) server.listen(80, () => { console.log('server running at http://www.example.com') })Copy the code
3.2 Implemented in Webpack
//webpack.config.js
​
module.exports = {
    ....
    devServer: {
        port: 80,
        proxy: {
          "/api": {
            target: "http://www.example.com"
          }
        }
    }
}
Copy the code

For details, see dev-server

3.3 Implemented in VUe-CLI
//vue.config.js
​
module.exports = {
    devServer: {
        proxy: {
          '/api': {
             target: 'http://www.example.com',
              changeOrigin: true,
              pathRewrite: {
                 '^/api': ''
              }
          }
        }
    }
}
Copy the code

For details, see DevServer-proxy

There are many other ways in which vue-CLI scaffolding can be implemented across domains, but I won’t go into detail here.

(4) Nginx reverse proxy

I don’t want to spend a lot of time on Nginx reverse proxies here, because there are concepts like liability equalization involved, as usual, with one diagram:

When the browser requests a reverse proxy server to fetch resources, the proxy server will go to all servers at that address to find an idle server to respond for you, which is used to balance the load rate of each server. Using an Nginx reverse proxy is actually a way of “tricking” the browser into thinking it is accessing a codomain, when in fact the server has “switched”. Directly on the code:

Install nginx first. For details about how to install nginx, see the nginx Installation tutorial

Then, modify the nginx.conf file in the nginx directory:

//nginx.conf server {# listen 80; Server_name http://www.abc.com; # forward the request to http://www.abc.com: 8080 proxy_pass http://www.abc.com: 8080; Compatible with websocket proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }}Copy the code

Finally, restart nginx on nginx -s reload.

Compared to CORS, Nginx has no browser version limitation and does not affect server performance.

(5) the Websocket

WebSocket is a communication protocol that prefixes ws:// (unencrypted) and WSS :// (encrypted). This protocol does not enforce the same origin policy and can be used for cross-source communication as long as the server supports it.

It establishes a “socket” connection between a web browser and a server. Simply put: There is a persistent connection between the client and the server, and both can start sending data at any time. Detailed tutorials can be seen:

Introduction to WebSocket: Introduces a socket to the network

There is not much explanation for this, so let’s go straight to the code:

Front end:

Const socket = new WebSocket('ws://www.abc.com') socket.onopen = () => {socket.send(' to send information... ')} socket.onMessage = (e) => {console.log(e.data) // Get data}Copy the code

Server:

const WebSocket = require("ws");
const server = new WebSocket.Server({ port: 80 });
server.on("connection", socket => {  
    socket.on("message", data => {    
        socket.send(data);  
    });
});
Copy the code
(6) the window. PostMessage

Here’s what MDN says about postMessage:

The window.postMessage() method can safely implement cross-source communication. In general, scripts with two different pages can only communicate with each other if the page executing them is on the same protocol (usually HTTPS), port number (443 is the default value for HTTPS), and host (the module document.domain of the two pages is set to the same value). The window.postmessage () method provides a controlled mechanism to circumvent this limitation and is safe as long as it is used correctly.

For example, the parent window http://aaa.com sends a message to the child window http://bbb.com by calling the postMessage method.

Const popup = window.open('http://bbb.com', 'title') popup. PostMessage (' Hello, BBB! ', 'http://bbb.com')Copy the code

The first argument to the postMessage method is the specific message content, and the second argument is the origin of the window that received the message, i.e. “protocol + domain + port”. You can also set this parameter to *, which indicates that the domain name is not limited and the message is sent to all Windows.

A child window sends a message to a parent window in a similar way

Window. Opener. PostMessage (' hello, aaa! ', 'http://aaa.com')Copy the code

Both parent and child Windows can listen for messages from each other through message events

window.addEventListener('message', Function (e) {console.log(e) {console.log(e) {console.log(e) {console.log(e)},false)Copy the code
(7) Document.domain + iframe

This method can be used only when the protocol, port, primary domain name (level 2 domain name) are the same and the subdomain name (level 1 domain name) is different, for example:

Document.domain is the base primary domain for both pages.

<! -- http://a.abc.com:8080/index.html --> <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial - scale = 1.0 "> < title > Document < / title > < / head > < body > I am http://a.abc.com:8080/index.html < iframe src="http://b.abc.com:8080/index.html" frameborder="0" onload="load()" id="iframe"></iframe> <script> document.domain = 'abc.com' function load() { console.log(iframe.contentWindow.a) //100 } </script> </body> </html>Copy the code
<! -- http://b.abc.com:8080/index.html --> <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial - scale = 1.0 "> < title > Document < / title > < / head > < body > I am http://b.abc.com:8080/index.html < script > Document. The domain =  'abc.com' let a = 100 </script> </body> </html>Copy the code
(8) window.location.hash + iframe

Because hashes are worth changing, pages are never refreshed, and this allows communication.

The parent window can write information to the fragment identifier of the child window

const src = 'http://www.abc.com/index.html#' + data
document.getElementById('iframe').src = src
Copy the code

The child window is notified by listening for the Hashchange event

window.onhashchange = checkMessage
​
function checkMessage() {
  let message = window.location.hash
  // ...
}
Copy the code

Similarly, child Windows can change the fragment identifier of the parent window

parent.location.href= target + "#" + hash
Copy the code
(9) window.name + iframe

The window.name attribute has a feature that the name value persists after loading on different pages (even different domain names) and can support very long name values (2MB).

Such as:

//www.aaa.com window.name = 'My name is aaa!!! ' setTimeout(function(){ window.location.href = "http://www.bbb.com" },1000)Copy the code

After the jump:

//www.bbb.com
​
console.log(window.name) //My name is aaa!!!
Copy the code

Visit http://www.aaa.com/index.html, http://www.bbb.com/index.html, can use a middle agent interface at http://www.aaa.com/other.html

<! -- http://www.aaa.com/index.html --> <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial - scale = 1.0 "> < title > Document < / title > < / head > < body > I am http://www.aaa.com/index.html < iframe src="http://www.aaa.com/other.html" frameborder="0" onload="load()" id="iframe"></iframe> <script> let first = true; // The onload event will fire twice, the first time to load the cross-domain page, And keep the data in the window. Name the function the load () {if (first) {. Iframe SRC = "http://www.bbb.com/index.html" first = false} else { The console. The log (iframe. ContentWindow. Name) / / I'm BBB}} < / script > < / body > < / HTML >Copy the code
<! -- http://www.aaa.com/other.html --> <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial - scale = 1.0 "> < title > Document < / title > < / head > < body > I am http://www.aaa.com/other.html < / body > < / HTML >Copy the code
<! -- http://www.bbb.com/index.html --> <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta  name="viewport" content="width=device-width, Initial - scale = 1.0 "> < title > Document < / title > < / head > < body > I am http://www.bbb.com/index.html < script > window. The name = 'I am BBB' </script> </body> </ HTML >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 bypass the browser’s cross-domain access restrictions, but at the same time it is a secure operation that liberalizes the same domain suffix as the Document.domain approach.

As for why you want to use an intermediate proxy interface, this is still due to the same origin policy problem, avoid error! Try it yourself if you don’t believe me.

(10) Modify the security Settings of the browser

This way is not recommended!!

This way is not recommended!!

This way is not recommended!!

After all, the security configuration of the browser is to prevent attacks, protection information is established, do not close easily. But some developers also use this “weird” method for native debugging.

Take Chrome, for example

Window
C:\Program Files\Google\Chrome\Application\chrome.exe --args --disable-web-security
Copy the code

Mac
open -a "Google Chrome" --args --disable-web-security  --user-data-dir
Copy the code
Linux
chromium-browser --disable-web-security  
Copy the code

Four,

This is a summary of 10 ways to solve cross-domain problems, and it should be said that it covers all ways to solve cross-domain problems. Which of these methods should be combined with the business scenario, I personally recommend choosing according to the following priorities:

  1. First of all, it is recommended to use CORS to solve cross-domain problems, because there is no need for redundant servers and no awareness in the foreground
  2. Node proxy and Nginx reverse proxy according to the server conditions to choose, generally use the former front-end more (scaffolding, etc.)
  3. If iframe is involved, postMessage is preferred, which is easier to operate

Five, the reference

  1. The same origin policy of the browser
  2. CORS
  3. Browser same origin policy and its circumvention method
  4. Cross-domain resource sharing (CORS
  5. 10 Cross-domain Solutions (with The Ultimate Trick)
  6. Nine Cross-domain Implementation Principles (Full version)