The essence of the problem
A typical case for web applications:
- frontend part is hosted on one server
- backend is represented with a monolith or several microservices (or their gateway) on another server (or servers)
In this case, whenever user interacts with this frontend to make a call to any microservice, browser is supposed to make a call to a server which domain name differs from the domain name of the frontend server, just like on the picture:
It is so called cross-origin request.
What is CORS
CORS (stands for Cross-Origin Resource Sharing) - this is an approach for a browser to find out whether web-application
with one origin
is allowed to get access to specific resources within different origin
. It is based on additional headers
that are sent by browser to different origin
.
In most of nowadays browsers there is enabled CORS policy which checks such kind of calls: if browser detects cross-origin calls it decides whether it has to be prechecked:
- method of the request can cause side-effects on server (usually
POST, PUT, DELETE
) - contains some custom headers
If request has to be prechecked, browser sends preflight OPTIONS
request. This request contains all necessary info like:
request type, origin, headers. Responsibility of the server is to check what are allowed values for that fields and send
back proper response with headers: Access-Control-Allow-Origin
, Access-Control-Request-Method
, Access-Control-Request-Headers
.
This response is used by the browser to verify whether it is allowed to make actual request to the server -
it checks whether current origin is among values listed in Access-Control-Allow-Origin
header and the same for methods and headers.
If our server is not properly configured and it does not return allowed values or browser fails CORS validation, we will
get an “Origin has been blocked by CORS policy” with “no ‘access-control-allow-origin’ header is present”
exception and it will look something like this for GET
and POST
requests:
Do we need to handle CORS?
The usual answer to the question “How to deal with CORS error in browser?” I found was to allow all origins to make calls. But at the same time, these CORS restrictions are needed to protect our users, so we have to find the way to deal with this CORS without relieving protection. To do this we have to configure our API layer to handle preflight requests properly.
NOTE: Sometimes for development purposes you might want to disable CORS, but please be sure that eventually it will be handled correctly.
The way to allow only specific origin in Spring boot application
Usually, the only thing you have to do in case of simple spring boot application is to add
@CrossOrigin
annotation to specific controller or method of the controller (you can have really
granular control over requests).
So it will look something like this:
NOTE: @CrossOrigin
annotation without value inside has *
as a default value.
Spring cloud gateway configuration of CORS
Common approach is to use gateway server which will handle requests to specific servers. Let’s see how we can configure our gateway in this case.
By default any cross-origin request is denied (for safety reasons). So we have to explicitly configure our server like this:
As you can see we have to configure each crucial part: allowed origins, allowed headers, allowed methods.
This configuration means that whenever gateway receives OPTIONS
request for any path it will
return similar response:
This response allows browser to send subsequent call.
Conclusion
CORS policy is meant to improve security for us and our users and we have to properly configure servers in order to handle it correctly. It is an additional layer of protection and it is never enough when we talk about these things. Moreover, we can see it is pretty easy to configure spring cloud gateway to handle cross-origin requests without the overhead or additional code written so there are no arguments not to do it.