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
Origin
field 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
true
means 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
XMLHttpRequest
objectgetResponseHeader()
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 theFooBar
field.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-Headers
field is required if the browser request includes theAccess-Control-Request-Headers
field. 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
GET
requests, 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