preface

Cross-domain is a common problem in front-end development. This article reviews four common cross-domain solutions and provides examples to illustrate the solutions.

Front knowledge

How do cookies work?

Cookies are key-value data stored in the browser and are used to maintain the login status.

The interaction process
  1. The user logs in to the site for the first time from the web, enters the account and password, and sends the request to the server.
  2. The server authenticates the password of the login request and generates a cookie after the authentication succeeds. The cookie is placed in the HTTP response packet and returned to the browser.
  3. After receiving the response packet, the browser stores the cookies in the packet locally.
  4. The next time a user visits a website, the request packet carries all the cookies that have been stored locally. The server uses the cookie in the request packet to verify whether the user has logged in.

The same-origin policy

Same-origin policy is an important security policy for browsers. It can help reduce the risk of malicious document attacks, such as XSS, CSRF, etc. Same-origin indicates that the protocol, domain name, and port are identical.

Limit content
  • Cookie, LocalStorage, Indexed DB, etc. (In this case, different domains cannot directly access each other’s storage content)
  • Ajax, XMLHttpRequest request
  • DOM node access

Cross domain

When domains request resources from each other, they are considered “cross-domains.”

Cross-domain labels are allowed
  • img
  • link
  • script
process

The cross-domain request was actually sent, the server received the request and returned the result, but the result was intercepted by the browser.

The solution

The prefix

  1. NPM init: Creates a repository
  2. NPM install express –save
  3. Use Express to host web pages locally and access the basic code:
let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
Copy the code

__dirname said that current path (exist index. HTML), in the browser side can be localhost: 3000 / index. HTML access hosting static pages.

jsonp

Principle: JSONP method does not have the vulnerability of cross-domain restriction through script tag, and can get the JSON data dynamically generated by other domains, but needs the support of the server.

Advantages and disadvantages: Only Get requests are supported, and other requests such as Post are not supported. It is troublesome to pass JSON data.

process
  1. The browser side creates a script tag and sets the SRC attribute of the tag. The SRC attribute is similar to the URL of a GET request. And pass a callback parameter to the server side in the request.

  2. The server receives the request, wraps the return data in the callback function parameter, and returns it to the front end as a new return data.

  3. After a successful return, the browser calls the callback, and the parameter in the callback is the data returned by the server. You can add processing of the returned data to the callback

The whole process is similar to the server-side calling the callback function passed by the front end and putting the data that the server-side needs to return in the parameters of the callback.

implementation
  • frontend/index.html
<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
  </head>
  <body>
    <script>
      function jsonp ({ url, params, callback }{
        return new Promise((resolve, reject) = > {
          // Create a script tag
          const script = document.createElement('script')
          // Mount the callback function on the window object
          window[callback] = function (data{
            resolve(data)
            document.body.removeChild(script) } params = { ... params, callback }// Generate the parameter URL part of the form: wd=b&c=show
          let arrs = []
          for (let key in params) {
            arrs.push(`${key}=${params[key]}`)}// Set the SRC tag of script
          script.src = `${url}?${arrs.join('&')}`
          document.body.appendChild(script)
        })
      }

      jsonp({
        url'http://localhost:3000/msg'.params: { msg'i_am_msg_from_client' },
        callback'show'
      }).then(data= > {
        console.log('Data returned from server:', data)
      })
    </script>
  </body>
</html>
Copy the code
  • backend/server.js
let express = require('express')
let app = express()
app.get('/msg'.(req, res) = > {
  console.log('req.query = ', req.query)
    const { wd, callback } = req.query
    console.log(wd)
    console.log(callback)
    res.end(`${callback}('i am msg from server')`)
})

app.listen(3000)
console.log('Server listening on port 3000... ')
Copy the code

cors

How it works: Set access-control-allow-Origin on the back end to enable CORS. This field indicates which domains have access to the resource. When the browser sends a request, it automatically adds an Origin field to the request, which represents the current field. The value of access-control-allow-Origin in the returned packet is the same as that of the Origin field in the current field. This means that the browser can access cross-domain resources.

  • frontend/index.html
<! DOCTYPEhtml>
<html lang="en">
  <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
      <title>Cors Cross-domain mode</title>
  </head>
  <body>
      <script>
          // Simple request GET
          let gXhr = new XMLHttpRequest();
          gXhr.open('GET'.'http://localhost:4000/getData'.true);
          gXhr.onreadystatechange = function({
              if(gXhr.readyState === 4 && gXhr.status === 200) {
                  console.log('Server successfully responded to GET request:', gXhr.response);
              }
          }
          gXhr.send();
      </script>
      <script>
          // Complex request PUT
          let xhr = new XMLHttpRequest();
          // Set cookie. Cookie cannot cross domains
          document.cookie = 'name=shenzhen';
          // The front end sets the request with cookies
          xhr.withCredentials = true;
          xhr.open('PUT'.'http://localhost:4000/getData'.true);
          xhr.setRequestHeader('name'.'shenzhen')
          xhr.onreadystatechange = function ({
              if (xhr.readyState === 4) {
                  if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
                      console.log(xhr.response)
                      // Get the response header, and the back end uses access-control-expox-headers to set the reponse response header
                      console.log(xhr.getResponseHeader('name'))
                  }
              }
          }
          xhr.send()
      </script>
  </body>
</html>
Copy the code
  • backend/server.js
let express = require('express');
let app = express();
// Set the whitelist, that is, set which domains can access the resources of the current domain
let whiteList = ['http://localhost:3000'];
app.use((req, res, next) = > {
    // When the front end sends an HTTP request, the browser will add the origin field in the header. The value of the field is the current field
    let origin = req.headers.origin;
    if(whiteList.includes(origin)) {
        // Set which source can access me
        res.setHeader('Access-Control-Allow-Origin', origin);
        // Which header is allowed to access me
        res.setHeader('Access-Control-Allow-Headers'.'name');
        // Which method is allowed to access me
        res.setHeader('Access-Control-Allow-Methods'.'PUT');
        // Allow cookies
        res.setHeader('Access-Control-Allow-Credentials'.true);
        // Precheck survival time
        res.setHeader('Access-Control-Max-Age'.6);
        // Allow return headers
        res.setHeader('Access-Control-Expose-Headers'.'name');
        
        if (req.method === 'OPTIONS') {
            res.end()   // OPTIONS requests are not processed
        }
    }
    next()
})

// Respond to the PUT request
app.put('/getData'.(req, res) = > {
    console.log(`server: ${req.headers}`)
    res.setHeader('name'.'jw');
    res.end('I am msg form server. Response for PUT request.')})// Respond to the GET request
app.get('/getData'.(req, res) = > {
    console.log(`server:${req.headers}`);
    res.end('I am msg from server. Response for GET request.')
})

app.use(express.static(__dirname));
app.listen(4000);
console.log('Server is listening on port 4000... ')
Copy the code
  • The results of

As you can see, in the order of execution in index.html. First, the get request is executed. It is a simple request. After setting access-control-Allow-Origin on the server, the data can be obtained normally. A PUT request is a complex request and requires a precheck options request. By comparing the origin in the options request, determine whether the current cross-domain is normal. After the verification passes, the actual PUT request is sent, and the returned data can be obtained.

Node server agent

The Node server proxy is implemented by setting up a proxy server between the browser and the target server to take care of forwarding. This is because there are no cross-domain issues when HTTP requests are made between servers. However, browsers and proxy servers need to address cross-domain issues.

  • frontend/index.html
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
    $.ajax({
        url'http://localhost:3000'.type'post'.data: { name'shenzhen'.password'123456' },
        contentType'application/json; charset=utf-8'.successfunction (result{
            console.log('receive msg from server: ', result)
        },
        errorfunction (msg{
            console.log(msg)
        }
    })
</script>
Copy the code

The browser sends a request to the proxy server.

  • mid_proxy_server/mid_server.js
const http = require('http')

// To receive the browser request, you need to use CORS to solve the cross-domain problem between the browser and the intermediate server
const server = http.createServer((request, response) = > {
    // The proxy server, which interacts directly with the browser, needs to set the CORS header field
    response.writeHead(200, {
        'Access-Control-Allow-Origin'The '*'.'Access-Control-Allow-Methods'The '*'.'Access-Control-Allow-Headers''Content-Type'
    })

    // Forward the request to the target server
    const proxyRequest = http.request({
        host'127.0.0.1'.port4000.url'/'.method: request.method,
        headers: request.headers
    }, serverResponse= > {
        var body = ' ';
        serverResponse.on('data'.chunk= > {
            body += chunk
        });

        serverResponse.on('end'.() = > {
            console.log('The data is ' + body)
            // Forward the response to the browser
            response.end(body)
        });
    }).end()
})

server.listen(3000.() = > {
    console.log('Intermediate proxy server running at http://localhost:3000')})Copy the code

The proxy server first needs to solve the cross-domain problem with the browser, through the backend setting access-control-allow-Origin. Upon receipt of the request, it forwards the request to the target server, which returns the result back to the browser.

Nginx proxy

The overall idea of Nginx is actually similar to that of Node middleware proxy, with proxy and forwarding being handled by Nginx.

  • Nginx. conf configuration file
# nginx reverse proxy server {listen 80; Server_name 127.0.0.1; Location / {proxy_pass http://127.0.0.1:4000; # reverse proxy proxy_cookie_domain http://127.0.0.1:4000 http://127.0.0.1; # Modify cookie index. HTML index. HTM; # When accessing Nignx using middleware proxy interfaces such as Webpack-dev-server, no browser is involved, so there is no same-origin restriction. Add_header access-control-allow-origin http://localhost:3000 is not enabled for the following cross-domain configuration; * add_header access-control-allow-credentials true; * add_header access-control-allow-credentials true; }}Copy the code
  • backend/server.js
var http = require('http')
var server = http.createServer()
var qs = require('querystring')
server.on('request'.function (req, res{
  console.log('Server receives forwarded request... ', req.url)
  var params = qs.parse(req.url.substring(2))
  var data = 'Login success! '
  // Write a cookie to the front desk
  res.writeHead(200, {
    'Set-Cookie''l=a123456; Path=/; Domain = 127.0.0.1:4000; HttpOnly'.// HttpOnly: The script cannot be read
  })
  res.write(JSON.stringify(data))
  res.end()
})
server.listen('4000')
console.log('Server is running at port 4000... ')
Copy the code

The source address

Four examples of cross-domain implementations

reference

  • Cookie function, interactive process parsing, setting, obtaining, deleting, effective time setting
  • Nine Cross-domain Implementation Principles (full version)
  • 15 stunning GIFs explaining CORS in its entirety
  • This command is used to start, stop, and restart Nginx in Windows