CORS explained in detail
CORS is a W3C standard, the full name is Cross-origin resource sharing.
It allows the browser to cross-origin server, issued XMLHttpRequest/fetch request, thus overcoming the AJAX can only be used in the same source of the limitations.
1. Introduction
CORS requires both browser and server support. Currently, all browsers support this feature, and Internet Explorer cannot be lower than IE10.
The entire CORS communication process is done automatically by the browser, without user involvement. For developers, CORS communication with the same source of AJAX communication no difference, the code is exactly the same. Once the browser finds the AJAX request cross-source, it will automatically add some additional headers, and sometimes one more additional request, but the user will not feel it.
Therefore, the key to achieve CORS communication is the server. As long as the server implements the CORS specification, it can communicate across sources.
2. Two types of requests
Browsers divide CORS requests into two categories: simple requests and not-so-simple requests.
As long as the following two conditions are met, it is a simple request.
-
The request method is one of the following three methods.
- HEAD
- GET
- POST
-
The HTTP headers do not exceed the following fields.
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type: limited to three values
application/x-www-form-urlencoded,multipart/form-data,text/plain
This is to be compatible with forms, because historically forms have been able to send cross-domain requests. AJAX’s cross-domain design is that, as long as the form can send, AJAX can send directly.
Where the above two conditions are not met at the same time, it is not a simple request.
The browser processing of these two requests, is not the same.
3. Simple request
3.1 Basic flow
For a simple request, the browser makes a CORS request directly. Specifically, it adds an Origin field to the header.
Here is an example, the browser finds that the cross-origin AJAX request is a simple request, it automatically adds an Origin field to the header information.
In the above header, the Origin field is used to indicate the source (protocol + domain + port) from which this request is coming. Based on this value, the server decides whether to grant the request or not.
If the source specified by Origin is not within the scope of the permission, the server returns a normal HTTP response. The browser finds that this response does not contain the Access-Control-Allow-Origin field in its headers (see below for details) and knows that there is an error, thus throwing an error that is caught by the onerror callback function of XMLHttpRequest. Note that such errors cannot be identified by status codes, as the status code of an HTTP response can be 200.
If the domain name specified by Origin is within the permitted range, the server returns a response with a few extra header fields.
The above header contains three fields related to CORS requests, all starting with Access-Control-.
-
Access-Control-Allow-Origin
This field is required. Its value is either the value of the
Originfield at the time of the request, or a*that indicates that a request for an arbitrary domain name is accepted. -
Access-Control-Allow-Credentials
This field is optional. Its value is a boolean indicating whether cookies are allowed to be sent, and by default, cookies are not included in CORS requests. A value of
truemeans that the server has explicitly given permission for the cookie to be included in the request and sent to the server together. This value can also only be set totrue, so if the server does not want the browser to send cookies, just delete the field. -
Access-Control-Expose-Headers
This field is optional. CORS request, the
XMLHttpRequestobjectgetResponseHeader()method can only get six basic fields:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma. If you want to get other fields, you must specify them in theAccess-Control-Expose-Headers. In the above example,getResponseHeader('FooBar')will successfully get the value of theFooBarfield.The wildcard character:
*, proposed in the latest specification, indicates that the client is allowed to access all header. is not yet implemented in some browsers.
3.2 withCredentials property
As mentioned above, CORS requests do not send cookies and HTTP authentication information by default. If a cookie is to be sent to the server, on the one hand, the server has to agree to respond to the Access-Control-Allow-Credentials field.
|
|
On the other hand, the developer must turn on the withCredentials property in the AJAX request
Otherwise, even if the server agrees to send a cookie, the browser will not send it. Or, if the server asks to set a cookie, the browser will not handle it.
However, some browsers will still send cookies together if the withCredentials setting is omitted, in which case withCredentials can be turned off explicitly.
Note that to send a cookie, Access-Control-Allow-Origin cannot be set to an asterisk, but must specify a domain name that is explicit and consistent with the requested page. At the same time, cookies still follow the same origin policy, only cookies set with the server domain will be uploaded, cookies from other domains will not be uploaded, and the document.cookie in the (cross-origin) original web code will not be able to read cookies from the server domain.
4. Non-simple requests
4.1 Preflight Requests
A non-simple request is one that has special requirements for the server, such as a request method of PUT or DELETE, or a Content-Type field of type application/json.
A CORS request that is not a simple request adds an HTTP query request, called a preflight, before the formal communication.
The browser first asks the server if the domain name of the current web page is in the server’s permission list, and which HTTP verbs and header fields can be used. Only when it gets a positive answer does the browser issue a formal XMLHttpRequest request, otherwise it throws an exception.
Here is a JavaScript script for the browser.
In the above code, the HTTP request method is PUT and sends a custom header X-Custom-Header.
The browser finds that this is a non-simple request and automatically issues a “preflight” request to ask the server to confirm that it can do so. Here is the HTTP header for this “preflight” request.
The request method used for the “preflight” request is OPTIONS, which indicates that the request is for interrogation. Inside the header, the key field is Origin, which indicates the source of the request.
In addition to the Origin field, the header of a “preflight” request includes two special fields.
-
Access-Control-Request-Method
This field is required to list which HTTP methods will be used by the browser’s CORS request, in the above example
PUT. -
Access-Control-Request-Headers
This field is a comma-separated string that specifies the additional header field that will be sent by the browser CORS request, in the above example
X-Custom-Header.
4.2 Response to Preflight Requests
After the server receives the Preflight request, it checks the Origin, Access-Control-Request-Method and Access-Control-Request-Headers fields and confirms that cross-origin requests are allowed, then it can respond.
|
|
The key thing in the HTTP response above is the Access-Control-Allow-Origin field, which indicates that http://api.bob.com is allowed to initiate the request. This field can also be set to an asterisk to indicate consent to any cross-origin request.
|
|
If the server denies the preflight request, a normal HTTP response is returned, but without any CORS-related header fields. At this point, the browser determines that the server did not agree to the preflight request and therefore triggers an error that is caught by the onerror callback function of the XMLHttpRequest object. The console will print the following error message.
The other CORS-related fields that the server responds to are listed below.
-
Access-Control-Allow-Methods
This field is required and its value is a comma-separated string indicating all methods supported by the server for cross-domain requests. Note that all supported methods are returned, not just the one requested by the browser. This is to avoid multiple “preflight” requests.
-
Access-Control-Allow-Headers
The
Access-Control-Allow-Headersfield is required if the browser request includes theAccess-Control-Request-Headersfield. It is also a comma-separated string indicating all header fields supported by the server, not limited to the fields requested by the browser in the Preflight. -
Access-Control-Allow-Credentials
This field has the same meaning as in the case of a simple request.
-
Access-Control-Max-Age
This field is optional and is used to specify the validity of this preflight request in seconds. In the above result, the validity period is 20 days (1728000 seconds), which means that the response is allowed to be cached for 1728000 seconds (i.e. 20 days), during which time another preflight request does not have to be issued.
4.3 Normal browser request and response
Once the server has passed the “preflight” request, each subsequent normal browser CORS request will have an Origin header field, just like a simple request. The server’s response will also have an Access-Control-Allow-Origin header field.
Here is the normal CORS request from the browser after the “preflight” request.
The Origin field in the above header is automatically added by the browser.
Here is the normal response from the server.
In the above header, the Access-Control-Allow-Origin field is always included in every response.
CORS is used for the same purpose as JSONP, but is more powerful than JSONP. JSONP only supports
GETrequests, while CORS supports all types of HTTP requests. the advantages of JSONP are support for older browsers and the ability to request data from sites that do not support CORS.
Spring With CORS
spring provides a number of ways to implement CORS.
@CrossOrigin
CORS can be easily implemented using the @CrossOrigin annotation. The code is as follows.
|
|
Note that When allowedCredentials is true, allowedOrigins cannot contain the special value * because it cannot be set in the Access-Control-Allow-Origin response header. To allow credentials from a set of sources, list them explicitly or consider using allowedOriginPatterns instead.
Otherwise, an exception will be thrown when the application starts.
|
|
WebMvcConfiguration
Global CORS configuration can be done through the addCorsMappings method of the WebMvcConfiguration configuration interface.
|
|
Similarly, if allowCredentials is true, allowedOrigins cannot be *.
CorsFilter
CorsFilter is specifically designed to handle CORS requests. This approach is the most flexible and allows you to write code to handle CORS requests.
|
|
Reference