preface

Related cross-domain concepts can be found in CORS MDN corresponding description.

This article will illustrate a few points of knowledge, and do some practical work with code examples to deepen understanding (rather than following baidu’s herd).

The same-origin policy

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.

Simply put, when we visit a website, the browser checks for different parts of the source address (protocol :// domain: port). Such as preventing the use of its source’s stored information (Cookies…) For unsafe use.

Cross-domain CORS

Any source identified by the browser is considered to be cross-domain and is not allowed by default.

For example, if you attempt to request a resource from http://127.0.0.1:3000 in http://127.0.0.1:4000, the following error occurs:

This is also a problem that the front end will encounter in 100% interface debugging.

Homologous and cross-domain judgment rules

Current browser address: http://domain/url

URL The results of why
http://domain/other homologous Address different
http://domain2 Cross domain Domain name is different
http://domain:8080 Cross domain Different ports
https://domain Cross domain Agreement is different

Simple and complex requests

Have you ever wondered why you see two requests from the same address in your browser’s Network? This is because requests can be divided into simple and complex requests.

Simple request: The cross-domain check is not triggered if the request meets the following conditions:

  • The request methods are GET, POST, and HEAD
  • Request headers: Accept, accept-language, content-language, content-Type

Content-type is limited to text/plain, multipart/form-data, Application/X-www-form-urlencoded

We can change the same origin rule, as shown in the following example:

At http://127.0.0.1:4000/, request the addresses of different ports at http://127.0.0.1:3000

The domain name is different. It’s cross-domain. But because the request method is GET, it fits the simple request and the request will work fine.

Complex request: Any request that does not meet simple requirements is complex. Before sending the request, a Preflight is made to the server using the Options method to know whether the server will allow the actual request.

Simulate a cross-domain request:

// Different ports, content-type is also undefined
axios.post(
  'http://127.0.0.1:3000/test/cors',
  {},
  {
    headers: {
      'content-type': 'application/json',}});Copy the code

You can see that the browser makes a Preflight request before the request:

This precheck request has a request method of options and contains the access-Control-xxx request header:

Of course, if the server does not do cross-domain processing (the example uses the service from Express, prechecking the request with a default response of 200), the browser CORS error warning will appear.

How to resolve cross-domain

For cross-domain, the front end is very familiar, baidu search can find a bunch of solutions, the keyword is not JSONP, or add some access-Control-XXX response header.

This article will cover the latter approach in detail, let’s call it a server-side solution.

Add the response header for options

In the Express example, we first add these response headers to the request from the OPTIONS method, which will cross-constrain the browser based on these attributes by telling it to:

app.use(function (req, res, next) {
  if (req.method == 'OPTIONS') {
    res.setHeader('Access-Control-Allow-Origin'.The '*');
    res.setHeader('Access-Control-Allow-Methods'.'GET, PUT, POST, DELETE, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers'.'content-type');
    res.status(200).end(); }});Copy the code

If you don’t set up the precheck interface correctly, it’s all in vain.

For example: If access-Control-allow-methods only sets POST and if the client request method is PUT, then you will end up with a cross-domain exception and indicate that PUT is not present in the access-Control-allow-methods precheck request:

Therefore, it is important to read cross-domain exceptions in the future to correctly add server response information. In addition:GET, POST, HEADMethods that belong to simple requests, so even if notAccess-Control-Allow-MethodsDefinitions are not in the way (point out if not)

Formal cross-domain request

Then add an additional cross-domain response header to the request made by our code (this needs to be consistent with the precheck interface above)

if (req.method == 'OPTIONS') {
  / /...
} else {
  / / http://127.0.0.1:3000/test/cors
  res.setHeader('Access-Control-Allow-Origin'.The '*');
  next();
}
Copy the code

Finally, we can see that we wait for the request to arrive properly:

A description of cross-domain request headers

Access-control-allow-origin = access-control-allow-methods = access-control-allow-headers = access-control-allow-headers = access-control-allow-headers = access-control-allow-origin = access-control-allow-methods = access-control-allow-headers

Refer to the CORS library, and there are other response headers for prechecking requests:

Head properties role
Access-Control-Allow-Origin Determine the source address (protocol :// Domain name: port)
Access-Control-Allow-Methods Limit method (GET, HEAD, PUT, PATCH, POST, DELETE)
Access-Control-Allow-Headers Qualified request headers (Content-Type)
Access-Control-Max-Age Cache time of precheck requests (in seconds, -1 is not cached)
Access-Control-Expose-Headers The response header available to the authorized client
Access-Control-Request-Headers The client generated request header
Access-Control-Allow-Credentials Restrict clients to carrying sensitive information
vary Define mutable headers to prevent browser caching

These heads will be explained below.

Access-Control-Allow-Origin

Both pre-check requests and normal requests inform the browser of the permitted source. Wildcard characters (*) are supported, but multi-source entries separated by commas (,) are not supported.

If you try more than one domain name, the following error occurs:

Response to preflight request doesn’t pass access control check: The ‘Access-Control-Allow-Origin’ header contains multiple values ‘aaa,bbb’, but only one is allowed.

Access-control-allow-origin is not recommended as a wildcard, which increases security risks. It is better to use the Origin of the requester.

const origin = req.headers.origin;
res.setHeader('Access-Control-Allow-Origin', origin || The '*');
// Since Origin changes depending on the Origin requested by the client, flag Vary so that the browser does not cache
res.setHeader('Vary'.'Origin');
Copy the code

Access-Control-Allow-Methods

The allowed Http method can be set as required, for example, GET, HEAD, PUT, PATCH, POST, or DELETE.

Because the default HTTP Methods for judging one of the simple requests are GET, POST, and HEAD, browsers support these even if they are not in the Access-Control-allow-methods convention.

For example, if the server defines the PUT method and the client sends the DELETE method, the following error occurs:

res.setHeader('Access-Control-Allow-Methods'.'PUT');
Copy the code

Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response.

Access-Control-Allow-Headers

The precheck interface informs the client of the allowed headers.

Request headers like simple request conventions support by default: Accept, accept-language, content-language, content-Type (Text /plain, multipart/form-data, Application/X-www-form-urlencoded)

If the client’s request header is outside the definition, an error is reported:

Request header field abc is not allowed by Access-Control-Allow-Headers in preflight response.

This header needs to be adjusted to:

res.setHeader('Access-Control-Allow-Headers'.'content-type, abc');
Copy the code

Access-Control-Max-Age

Define the precheck interface to tell clients how long allowed headers can be cached.

Default time rule:

  • In Firefox, the limit is 24 hours (86,400 seconds).
  • Prior to Chromium V76, the limit was 10 minutes (600 seconds).
  • Starting with Chromium V76, the limit is 2 hours (7,200 seconds).
  • Chromium also has a default value of 5 seconds.
  • If the value is -1, caching is disabled. In this case, OPTIONS is used to precheck requests before each request.

For example, after the parameter is set to 5 seconds, the client will not send the precheck interface for the first time within 5 seconds:

res.setHeader('Access-Control-Max-Age'.'5');
Copy the code

Access-Control-Allow-Credentials

For cross-domain requests, the default browser will not send the Cookies of the current address to the server to ensure the security of the information. The access-Control-allow-credentials response header needs to be set on the server, and the withCredentials configuration needs to be enabled on the client.

// Client request
axios.post(
  'http://127.0.0.1:3000/test/cors',
  {},
  {
    headers: {
      'content-type': 'application/json'.abc: '123',},withCredentials: true});Copy the code
// All requests
res.setHeader('Access-Control-Allow-Credentials'.'true');
Copy the code

Access-control-allow-origin cannot be set to a wildcard “*”.

thisAccess-Control-Allow-OriginMust be the address of the current page source.

Access-Control-Expose-Headers

Similar to access-Control-allow-credentials, if the server has a custom set of request headers, cross-domain client requests cannot receive these headers in the response information.

/ / the server
res.setHeader('def'.'123');
Copy the code
axios
  .post(
    'http://127.0.0.1:3000/test/cors',
    {},
    {
      headers: {
        'content-type': 'application/json'.abc: '123',},withCredentials: true,
    }
  )
  .then((data) = > {
    console.log(data.headers.def); //undefined
  });
Copy the code

You need to set the access-Control-expose-headers response Headers on the server and mark which Headers are available to the client:

res.setHeader('Access-Control-Expose-Headers'.'def');
res.setHeader('def'.'123');
Copy the code

Access-Control-Request-Headers

I can’t find an example of using access-Control-request-headers, which is actually concatenated from the current Request header.

If the client request header is:

{
  "content-type": "application/json"."abc": "123"."xyz": "123",},Copy the code

The browser will eventually add an Access-Control-Request-HEADERS header to the precheck interface, with values of ABC, Content-Type,xyz. The server then tells the browser server the support for the request Headers according to the access-Control-allow-headers statement. Finally, the browser determines whether there is a cross-domain error.

Vary Headers for access-Control-request-headers on the server:

res.setHeader('Vary'.'Origin' + ', ' + req.headers['access-control-request-headers']);
Copy the code

This gives you a basic idea of cross-domain and how headers are handled. We hope that similar problems can be solved methodically, rather than random attempts.