CORS
Overview
The cross-origin resource sharing (CORS) protocol helps developers control if and how REST resources served by their applications can be shared across origins. Helidon MP includes an implementation of CORS that you can use to add CORS behavior to the services you develop. You can define your application’s CORS behavior programmatically using the Helidon CORS API alone or together with configuration.
Before You Begin
Before you revise your application to add CORS support, you need to decide what
type of cross-origin sharing you want to allow for each resource your
application exposes. For example, suppose for a given resource you want to allow
unrestricted sharing for GET, HEAD, and POST requests (what CORS refers to as
"simple" requests), but permit other types of requests only from the two origins
foo.com and there.com. Your application would implement two types of CORS
sharing: more relaxed for the simple requests and stricter for others.
Once you know the type of sharing you want to allow for each of your resources, including any from built-in services, you can change your application accordingly.
Maven Coordinates
To enable CORS, add the following dependency to your project’s pom.xml (see
Managing Dependencies).
<dependency>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-cors</artifactId>
</dependency>
Usage
Once you have planned how each of your resources should support CORS, you specify the CORS behavior in one of two ways:
- add
@Cors.*annotations to the Java code for the resources, or - add configuration.
You can do both. CORS configuration for a resource overrides any CORS settings
declared using @Cors.* in the Java class for the resource.
API
Annotations
Adding CORS behavior to your Helidon MP application involves just a few simple steps.
For each resource class in your application:
- Identify the resources and sub-resources, in other words, the paths declared in the resource class which you want to support CORS.
- For each of those resources and sub-resources which should support CORS:
- Find or create a Java method annotated with
@OPTIONSand with the correct@Path. - To that
@OPTIONSJava method add a Helidon@Cors.*annotation(s) that describes the cross-origin sharing you want for that resource.
- Find or create a Java method annotated with
@Cors.* annotations only on methods which also have the @OPTIONS
annotation. Remember that the CORS settings apply to a given path and
therefore to all Java resource methods which share that path.Helidon MP aborts the server start-up if you use the @Cors.* annotations on
a resource method other than an @OPTIONS method.
For an informal look at the reasons for applying the @Cors.* annotations to
the @OPTIONS method, instead of another method, see
Why@OPTIONS?.
The following annotations are available:
@Cors.Defaults- has no values, applies all defaults (do not combine with annotations below)@Cors.AllowOrigins- value is the allowed origins, defaults to all origins@Cors.AllowHeaders- value is the allowed HTTP header names, defaults to all headers@Cors.AllowMethods- value is the allowed HTTP method names, defaults to all methods@Cors.ExposeHeaders- value is the exposed HTTP header names, defaults to none@Cors.AllowCredentials- value is a boolean, defaults to false@Cors.MaxAgeSeconds- value is the max age as a number of seconds
Configuration
You can define CORS behavior and you or your users can override behavior declared in your code using configuration.
For each resource you want to configure, add a section to
META-INF/microprofile-config.properties file:
General form of CORS configuration:
The system uses the index i, not the position in the config file, to identify
the settings for a particular resource.
Path patterns can be any expression accepted by the PathMatcher
class.
Each annotation in Cors class (except for Defaults) is mapped to one of the
configuration options, see details below:
Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
allow- | List< | * | Set of allowed headers, defaults to all |
allow- | Boolean | false | Whether to allow credentials |
max- | String | PT1H | Max age as a duration |
allow- | List< | * | Set of allowed origins, defaults to all |
expose- | List< | Set of exposed headers, defaults to none | |
path- | String | Path pattern to apply this configuration for | |
allow- | List< | * | Set of allowed methods, defaults to all |
enabled | Boolean | true | Whether this CORS configuration should be enabled or not |
Examples
The Helidon MP Quickstart application allows users to:
- obtain greetings by sending
GETrequests to the/greetresource, and - change the greeting message by sending a
PUTrequest to the/greet/greetingresource.
The Helidon MP CORS Example shows the basic quickstart example enhanced for CORS.
The discussion below describes the changes in the application which:
- permit unrestricted sharing of the resource
/greet, and - restrict sharing of the resource
/greet/greetingso that only the originshttp://foo.comandhttp://there.comcan change the greeting.
Adding Annotations
Using annotations to declare CORS behavior:
Adding Configuration
You could use the following configuration in place of using annotations to set up the same CORS behavior.
Using configuration to set up the same CORS behavior:
Or, alternatively, the following configuration example augments the settings
from the @Cors.* annotations in the code.
Using configuration to augment or override declared CORS behavior:
Additional Information
Requested URI
The decisions the Helidon CORS feature makes depend on accurate information about each incoming request, particularly the host to which the request is sent. Conveyed as headers in the request, this information can be changed or overwritten by intermediate nodes such as load balancers between the origin of the request and your service.
Well-behaved intermediate nodes preserve this important data in other headers,
such as Forwarded. You can configure how the Helidon server handles these
headers as described in the documentation for requested URI
discovery.
The CORS support in Helidon uses the requested URI feature to discover the correct information about each request, according to your configuration, so it can make accurate decisions about whether to permit cross-origin accesses.
Built-in Services
Use configuration to control whether and how each of the built-in services works with CORS.
In the cors configuration section add a block for each built-in service using
its path as described in the CORS configuration section.
The following example restricts sharing of
- the
/healthresource, provided by the health built-in service, to only the originhttps://there.com, and - the
/metricsresource, provided by the metrics built-in service, to only the originhttps://foo.com.
Configuration which restricts sharing of the health and metrics resources:
cors.paths.0.path-pattern=/health
cors.paths.0.allow-origins=https://there.com
cors.paths.1.path-pattern=/metrics
cors.paths.1.allow-origins=https://foo.com
Shared Resources
If you have edited the Helidon MP QuickStart application as described in the
previous topics and saved your changes, you can build and run the application.
Once you do so you can execute curl commands to demonstrate the behavior
changes in the metric and health services with the addition of the CORS
functionality. Note the addition of the Origin header value in the curl
commands, and the Access-Control-Allow-Origin in the successful responses.
Build and Run the Application
Build and run the QuickStart application as usual.
mvn package
java -jar target/helidon-quickstart-mp.jar
2020.05.12 05:44:08 INFO io.helidon.microprofile.server.ServerCdiExtension Thread[main,5,main]: Server started on http://localhost:8080 (and all other host addresses) in 5280 milliseconds (since JVM startup).
Retrieve Metrics
The metrics service rejects attempts to access metrics on behalf of a disallowed origin.
curl -i -H "Origin: https://other.com" http://localhost:8080/metrics
HTTP/1.1 403 Forbidden
Date: Mon, 11 May 2020 11:08:09 -0500
transfer-encoding: chunked
connection: keep-alive
But accesses from foo.com succeed.
curl -i -H "Origin: https://foo.com" http://localhost:8080/metrics
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://foo.com
Content-Type: text/plain
Date: Mon, 11 May 2020 11:08:16 -0500
Vary: Origin
connection: keep-alive
content-length: 6065
# TYPE base_classloader_loadedClasses_count gauge
# HELP base_classloader_loadedClasses_count Displays the number of classes that are currently loaded in the Java virtual machine.
base_classloader_loadedClasses_count 3568
Retrieve Health
The health service rejects requests from origins not specifically approved.
curl -i -H "Origin: https://foo.com" http://localhost:8080/health
HTTP/1.1 403 Forbidden
Date: Mon, 11 May 2020 12:06:55 -0500
transfer-encoding: chunked
connection: keep-alive
And responds successfully only to cross-origin requests from
https://there.com.
curl -i -H "Origin: https://there.com" http://localhost:8080/health
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://there.com
Content-Type: application/json
Date: Mon, 11 May 2020 12:07:32 -0500
Vary: Origin
connection: keep-alive
content-length: 461
{"outcome":"UP",...}