Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/openliberty/guide-cors

A guide on how to enable Cross-Origin Resource Sharing (CORS) in Open Liberty: https://openliberty.io/guides/cors.html
https://github.com/openliberty/guide-cors

Last synced: 2 months ago
JSON representation

A guide on how to enable Cross-Origin Resource Sharing (CORS) in Open Liberty: https://openliberty.io/guides/cors.html

Awesome Lists containing this project

README

        

// Copyright (c) 2017, 2023 IBM Corporation and others.
// Licensed under Creative Commons Attribution-NoDerivatives
// 4.0 International (CC BY-ND 4.0)
// https://creativecommons.org/licenses/by-nd/4.0/
//
// Contributors:
// IBM Corporation
:projectid: cors
:page-layout: guide-multipane
:page-duration: 15 minutes
:page-releasedate: 2017-10-12
:page-description: Learn how to enable CORS in Open Liberty without writing Java code to access resources from an external domain.
:page-permalink: /guides/{projectid}
:page-related-guides: ['rest-intro', 'rest-client-java']
:common-includes: https://raw.githubusercontent.com/OpenLiberty/guides-common/prod/
:source-highlighter: prettify
:page-seo-title: Enabling Cross-Origin Resource Sharing (CORS) in a RESTful Java microservice
:page-seo-description: A tutorial with examples on how to add server configurations to enable Cross-Origin Resource Sharing (CORS) in a Java microservice. Learn how to send simple and preflight CORS requests and inspect the HTTP request and response headers.
:guide-author: Open Liberty
= Enabling Cross-Origin Resource Sharing (CORS)

[.hidden]
NOTE: This repository contains the guide documentation source. To view the guide in published form, view it on the https://openliberty.io/guides/{projectid}.html[Open Liberty website].

Learn how to enable Cross-Origin Resource Sharing (CORS) in Open Liberty without writing Java code.

// =================================================================================================
// What you'll learn
// =================================================================================================

== What you'll learn

You will learn how to add two Liberty configurations to enable CORS. Next, you will write and run tests to validate that the CORS configurations work. These tests send two different CORS requests to a REST service that has two different endpoints.

=== CORS and its purpose

Cross-Origin Resource Sharing (CORS) is a W3C specification and mechanism that you can use to request restricted resources from a domain outside the current domain. In other words, CORS is a technique for consuming an API served from an origin different than yours.

CORS is useful for requesting different kinds of data from websites that aren't your own. These types of data might include images, videos, scripts, stylesheets, iFrames, or web fonts.

However, you cannot request resources from another website domain without proper permission. In JavaScript, cross-origin requests with an `XMLHttpRequest` API and Ajax cannot happen unless CORS is enabled on the server that receives the request. Otherwise, same-origin security policy prevents the requests. For example, a web page that is served from the `\http://aboutcors.com` server sends a request to get data to the `\http://openliberty.io` server. Because of security concerns, browsers block the server response unless the server adds HTTP response headers to allow the web page to consume the data.

Different ports and different protocols also trigger CORS. For example, the `\http://abc.xyz:1234` domain is considered to be different from the `\https://abc.xyz:4321` domain.

Open Liberty has built-in support for CORS that gives you an easy and powerful way to configure the runtime to handle CORS requests without the need to write Java code.

=== Types of CORS requests

Familiarize yourself with two kinds of CORS requests to understand the attributes that you will add in the two CORS configurations.

==== Simple CORS request

According to the CORS specification, an HTTP request is a simple CORS request if the request method is `GET`, `HEAD`, or `POST`. The header fields are any one of the `Accept`, `Accept-Language`, `Content-Language`, or `Content-Type` headers. The `Content-Type` header has a value of `application/x-www-form-urlencoded`, `multipart/form-data`, or `text/plain`.

When clients, such as browsers, send simple CORS requests to servers on different domains, the clients include an `Origin` header with the original (referring) host name as the value. If the server allows the origin, the server includes an `Access-Control-Allow-Origin` header with a list of allowed origins or an asterisk (*) in the response back to the client. The asterisk indicates that all origins are allowed to access the endpoint on the server.

==== Preflight CORS request

A CORS request is not a simple CORS request if a client first sends a preflight CORS request before it sends the actual request. For example, the client sends a preflight request before it sends a `DELETE` HTTP request. To determine whether the request is safe to send, the client sends a preflight request, which is an `OPTIONS` HTTP request, to gather more information about the server. This preflight request has the `Origin` header and other headers to indicate the HTTP method and headers of the actual request to be sent after the preflight request.

Once the server receives the preflight request, if the origin is allowed, the server responds with headers that indicate the HTTP methods and headers that are allowed in the actual requests. The response might include more CORS-related headers.

Next, the client sends the actual request, and the server responds.

// =================================================================================================
// Getting Started
// =================================================================================================
[role=command]
include::{common-includes}/gitclone.adoc[]

// =================================================================================================
// Enabling CORS
// =================================================================================================

== Enabling CORS
Navigate to the `start` directory to begin.
// cloud hosted instructions
ifdef::cloud-hosted[]
```bash
cd /home/project/guide-cors/start
```
endif::[]

[role='command']
include::{common-includes}/devmode-lmp33-start.adoc[]

You will use a REST service that is already provided for you to test your CORS configurations. You can find this service in the `src/main/java/io/openliberty/guides/cors/` directory.

You will send a simple request to the `/configurations/simple` endpoint and the preflight request to the `/configurations/preflight` endpoint.

// =================================================================================================
// Enabling a simple CORS configuration
// =================================================================================================

=== Enabling a simple CORS configuration
Configure the Liberty to allow the `/configurations/simple` endpoint to accept a [hotspot=simple-config file=0]`simple` CORS request. Add a simple CORS configuration to the Liberty [hotspot file=0]`server.xml` configuration file:

[role="code_command hotspot file=0", subs="quotes"]
----
#Replace the Liberty `server.xml` configuration file.#
`src/main/liberty/config/server.xml`
----
server.xml
[source, xml, linenums, role='code_column hide_tags=preflight-config']
----
include::finish/src/main/liberty/config/server.xml[]
----

The CORS configuration contains the following attributes:

[cols="1, 2", options="header"]
|===
| *Configuration Attribute* | *Value*
|[hotspot=17 file=0]`domain` | The endpoint to be configured for CORS requests. The value is set to `/configurations/simple`.
|[hotspot=18 file=0]`allowedOrigins` | Origins that are allowed to access the endpoint. The value is set to `\http://openliberty.io`.
|[hotspot=19 file=0]`allowedMethods` | HTTP methods that a client is allowed to use when it makes requests to the endpoint. The value is set to `GET`.
|[hotspot=20 file=0]`allowCredentials` | A boolean that indicates whether the user credentials can be included in the request. The value is set to `true`.
|[hotspot=21 file=0]`exposeHeaders` | Headers that are safe to expose to clients. The value is set to `MyHeader`.
|===

For more information about these and other CORS attributes, see the https://www.openliberty.io/docs/latest/reference/config/cors.html[cors element documentation^].

Save the changes to the [hotspot file=0]`server.xml` configuration file. The `/configurations/simple` endpoint is now ready to be tested with a simple CORS request.

The Open Liberty instance was started in dev mode at the beginning of the guide and all the changes were automatically picked up.

Now, test the simple CORS configuration that you added. Add the [hotspot=testSimpleCorsRequest file=1]`testSimpleCorsRequest` method to the `CorsIT` class.

[role="code_command hotspot file=1", subs="quotes"]
----
#Replace the `CorsIT` class.#
`src/test/java/it/io/openliberty/guides/cors/CorsIT.java`
----

CorsIT.java
[source, java, linenums, role='code_column hide_tags=copyright,testPreflightCorsRequest']
----
include::finish/src/test/java/it/io/openliberty/guides/cors/CorsIT.java[]
----

The [hotspot=testSimpleCorsRequest file=1]`testSimpleCorsRequest` test simulates a client. It first sends a simple CORS request to the `/configurations/simple` endpoint, and then it checks for a valid response and expected headers. Lastly, it prints the response headers for you to inspect.

The request is a [hotspot=get file=1]`GET` HTTP request with the following header:

[cols="1, 2", options="header"]
|===
| *Request Header* | *Request Value*
| Origin | The value is set to `\http://openliberty.io`. Indicates that the request originates from `\http://openliberty.io`.
|===

Expect the following response headers and values if the simple CORS request is successful, and the Liberty instance is correctly configured:

[cols="1, 2", options="header"]
|===
| *Response Header* | *Response Value*
| Access-Control-Allow-Origin | The expected value is `\http://openliberty.io`. Indicates whether a resource can be shared based on the returning value of the Origin request header `\http://openliberty.io`.
| Access-Control-Allow-Credentials | The expected value is `true`. Indicates that the user credentials can be included in the request.
| Access-Control-Expose-Headers | The expected value is `MyHeader`. Indicates that the header `MyHeader` is safe to expose.
|===

Because you started Open Liberty in dev mode, you can run the tests by pressing the `enter/return` key from the command-line session where you started dev mode.

If the [hotspot=testSimpleCorsRequest file=1]`testSimpleCorsRequest` test passes, the response headers with their values from the endpoint are printed. The `/configurations/simple` endpoint now accepts simple CORS requests.

Response headers with their values from the endpoint:
[role='no_copy']
```
--- Simple CORS Request ---
Header null = [HTTP/1.1 200 OK]
Header Access-Control-Expose-Headers = [MyHeader]
Header Access-Control-Allow-Origin = [http://openliberty.io]
Header Access-Control-Allow-Credentials = [true]
Header Content-Length = [22]
Header Content-Language = [en-CA]
Header Date = [Thu, 21 Mar 2019 17:50:09 GMT]
Header Content-Type = [text/plain]
Header X-Powered-By = [Servlet/4.0]
```
// =================================================================================================
// Enabling a preflight CORS configuration
// =================================================================================================

=== Enabling a preflight CORS configuration

Configure the Liberty to allow the `/configurations/preflight` endpoint to accept a [hotspot=preflight-config file=0]`preflight` CORS request. Add another CORS configuration in the Liberty `server.xml` configuration file:

[role="code_command hotspot file=0", subs="quotes"]
----
#Replace the Liberty `server.xml` configuration file.#
`src/main/liberty/config/server.xml`
----

server.xml
[source, xml, linenums, role='code_column']
----
include::finish/src/main/liberty/config/server.xml[]
----

The preflight CORS configuration has different values than the simple CORS configuration.

[cols="1, 2", options="header"]
|===
| *Configuration Attribute* | *Value*
| [hotspot=25 file=0]`domain`|The value is set to `/configurations/preflight` because the `domain` is a different endpoint.
| [hotspot=26 file=0]`allowedOrigins`| Origins that are allowed to access the endpoint. The value is set to an asterisk (*) to allow requests from all origins.
| [hotspot=27 file=0]`allowedMethods`| HTTP methods that a client is allowed to use when it makes requests to the endpoint. The value is set to `OPTIONS, DELETE`.
| [hotspot=28 file=0]`allowCredentials`| A boolean that indicates whether the user credentials can be included in the request. The value is set to `true`.
|===

The following attributes were added:

* [hotspot=29 file=0]`allowedHeaders`: Headers that a client can use in requests. Set the value to `MyOwnHeader1, MyOwnHeader2`.
* [hotspot=30 file=0]`maxAge`: The number of seconds that a client can cache a response to a preflight request. Set the value to `10`.

Save the changes to the [hotspot file=0]`server.xml` configuration file. The `/configurations/preflight` endpoint is now ready to be tested with a preflight CORS request.

Add another test to the [hotspot file=1]`CorsIT.java` file to test the preflight CORS configuration that you just added:

[role="code_command hotspot file=1", subs="quotes"]
----
#Replace the `CorsIT` class.#
`src/test/java/it/io/openliberty/guides/cors/CorsIT.java`
----

CorsIT.java
[source, java, linenums, role='code_column hide_tags=copyright']
----
include::finish/src/test/java/it/io/openliberty/guides/cors/CorsIT.java[]
----

The [hotspot=testPreflightCorsRequest file=1]`testPreflightCorsRequest` test simulates a client sending a preflight CORS request. It first sends the request to the `/configurations/preflight` endpoint, and then it checks for a valid response and expected headers. Lastly, it prints the response headers for you to inspect.

The request is an [hotspot=options file=1]`OPTIONS` HTTP request with the following headers:

[cols="1, 2", options="header"]
|===
| *Request Header* | *Request Value*
| Origin | The value is set to `anywebsiteyoulike.com`. Indicates that the request originates from `anywebsiteyoulike.com`.
| Access-Control-Request-Method | The value is set to `DELETE`. Indicates that the HTTP DELETE method will be used in the actual request.
| Access-Control-Request-Headers | The value is set to `MyOwnHeader2`. Indicates the header `MyOwnHeader2` will be used in the actual request.
|===

Expect the following response headers and values if the preflight CORS request is successful, and the Liberty instance is correctly configured:

[cols="1, 2", options="header"]
|===
| *Response Header* | *Response Value*
| Access-Control-Max-Age | The expected value is `10`. Indicates that the preflight request can be cached within `10` seconds.
| Access-Control-Allow-Origin | The expected value is `anywebsiteyoulike.com`. Indicates whether a resource can be shared based on the returning value of the Origin request header `anywebsiteyoulike.com`.
| Access-Control-Allow-Methods | The expected value is `OPTIONS, DELETE`. Indicates that HTTP OPTIONS and DELETE methods can be used in the actual request.
| Access-Control-Allow-Credentials | The expected value is `true`. Indicates that the user credentials can be included in the request.
| Access-Control-Allow-Headers | The expected value is `MyOwnHeader1, MyOwnHeader2`. Indicates that the header `MyOwnHeader1` and `MyOwnHeader2` are safe to expose.
|===

The `Access-Control-Allow-Origin` header has a value of `anywebsiteyoulike.com` because the Liberty is configured to allow all origins, and the request came with an origin of `anywebsiteyoulike.com`.

Because you started Open Liberty in dev mode, you can run the tests by pressing the `enter/return` key from the command-line session where you started dev mode.

If the [hotspot=testPreflightCorsRequest file=1]`testPreflightCorsRequest` test passes, the response headers with their values from the endpoint are printed. The `/configurations/preflight` endpoint now allows preflight CORS requests.

Response headers with their values from the endpoint:
[role='no_copy']
```
--- Preflight CORS Request ---
Header null = [HTTP/1.1 200 OK]
Header Access-Control-Allow-Origin = [anywebsiteyoulike.com]
Header Access-Control-Allow-Methods = [OPTIONS, DELETE]
Header Access-Control-Allow-Credentials = [true]
Header Content-Length = [0]
Header Access-Control-Max-Age = [10]
Header Date = [Thu, 21 Mar 2019 18:21:13 GMT]
Header Content-Language = [en-CA]
Header Access-Control-Allow-Headers = [MyOwnHeader1, MyOwnHeader2]
Header X-Powered-By = [Servlet/4.0]
```

You can modify the Liberty configuration and the test code to experiment with the various CORS configuration attributes.

[role='command']
include::{common-includes}/devmode-quit-ctrlc.adoc[]

// =================================================================================================
// Congratulations! You're done!
// =================================================================================================

== Great work! You're done!

You enabled CORS support in Open Liberty. You added two different CORS configurations to allow two kinds of CORS requests in the Liberty `server.xml` configuration file.

include::{common-includes}/attribution.adoc[subs="attributes"]