Introduction
We have seen in a previous article how to provide basic security to a Spring Cloud system using SSL. We have seen how to encrypt data sent and received by external users and applications, data exchanged between internal services, and how to make these last ones mutually trusted. This article will discuss how to protect web resources and grant access only to trusted users and programs using the OAuth2 standard in a Spring Cloud system.
OAuth2 Generalities
OAuth2 is an authorization protocol. Oauth2 uses access tokens to handle access to protected resources, like REST services or user data. It doesn’t say anything about authentication, but its implementations can also provide this functionality. For example, for our demonstration, we will use Keycloak, which offers both OAuth2 and single sign-on authentication among its features.
OAuth2 can be implemented by several components, with the following roles:
- Resource Owner: It is the owner of the resource and can grant access to them
- Client: The client is an application that has to access a resource. It can do it by obtaining an access token
- Authorization Server: It returns a token to the client if the resource owner grants accessì
- Resource Server: It hosts resources. It gives clients access to a resource if it finds that the token passed by the client is valid
OAuth2 Sample
We will demonstrate how OAuth2 can be used in a Spring Cloud system. We will consider a simple web application named book-service, hidden behind a Spring Boot gateway, meant to return information about books and use Keycloak as an authorization and authentication server.
In our example, the gateway plays both the roles of the client and the authorization server, backed by Keycloak. As a client, when a request arrives to get a web resource (a REST service in our example), it calls the Keycloak instance to get an access token. If the client is not authenticated, a login page is presented to the user, otherwise the token is forwarded by the gateway to the web application.
Run and Configure Keycloak
To run an instance of Keycloak we can use docker, issuing the following command:
$ docker run -d --name keycloak -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:23.0.7 start-dev
We have to go to http://localhost:8080/admin, and log in with the given user and password, admin/admin. We then have to create a realm, we call it just “example”:

In the realm “example” we create a client, and we call it “gateway-client”, setting the Client authentication property to On. We also set the Valid redirect URIs parameter to “*”.


In the credential section of the client configuration panel, we set a client secret:

Finally, we create a user named “test” with password “test”:

This will be the username that the resource owner uses to authenticate and then authorize the client.
Configure the Gateway with OAuth2
Besides its usual role, the gateway acts as an OAuth2 client and resource server. Therefore we have to add the following dependencies:
<!-- Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Oauth2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
We have to annotate a configuration class with @EnableWebFluxSecurity, to activate security features based on the reactive version of Spring Web, since the gateway server is based on that. We also define a SecurityWebFilterChain that uses an oauth2Login method to present a login page to the unauthenticated client and oauth2ResourceServer to check the access token before the request is forwarded to the inner services.
@Configuration
@EnableWebFluxSecurity
class AppConfiguration {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange(auth -> auth.anyExchange()
.authenticated())
.oauth2Login(withDefaults())
.oauth2ResourceServer((oauth2) -> oauth2.jwt(withDefaults()));
http.csrf(ServerHttpSecurity.CsrfSpec::disable);
return http.build();
}
}
In the application.yaml file we set the gateway configuration, as you can see in the following fragment. We set the routes to the two services and add the TokenRelay value to the default-filters section: this ensures that the token is forwarded from the gateway to the services. We have also to set several parameters for the OAuth2 configuration. The jwk-set-uri is set to an endpoint of Keycloak that checks the access token. The issuer-uri parameter contains the URI of the Keycloak provider. Finally, we have the credentials of the client, with the secret id, and the name of the provider.
spring:
application:
name: gateway-service
cloud:
gateway:
default-filters:
- TokenRelay=
routes:
- id: author-service
uri: http://localhost:8091
predicates:
- Path=/authors/**
- id: book-service
uri: http://localhost:8092
predicates:
- Path=/library/**
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://localhost:8080/realms/test/protocol/openid-connect/certs
client:
provider:
keycloak:
issuer-uri: http://localhost:8080/realms/test
registration:
gateway-client:
provider: keycloak
client-id: gateway-client
client-secret: VvQXimbn43QmNLljL7f4EvWiBdXTLtRa
authorization-grant-type: authorization_code
scope: openid
We can make the gateway return the access token by using an OAuth2AuthorizedClient instance with an endpoint like the following:
@GetMapping(value = "/getAccessToken")
public String getHome(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient) {
return authorizedClient.getAccessToken()
.getTokenValue();
}
Configure the Web Application with OAuth2
We need the same OAuth2 dependencies as the gateway for the web application. In addition, we add the web and security starters.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
We use the @EnableWebSecurity annotation since the service uses a standard, not reactive web module. Like we did for the gateway, we set a filter with the oauth2ResourceServer method, but we don’t need the oauth2Login one, because it is the responsibility of the gateway to redirect the client to the login page.
@Configuration
@EnableWebSecurity
public class AppConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorize -> authorize.anyRequest()
.authenticated())
.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}
The OAuth2 configuration in the application.yaml file is the same as the gateway. To test the application we define a REST endpoint:
@RestController
@RequestMapping("/library")
public class BookController {
Logger logger = LoggerFactory.getLogger(BookController.class);
@GetMapping(value = "/bookOfTheMonth")
public String getBookOfTheMonth() {
return "The naked sun";
}
}
Run the Demo
We can test the example by running the gateway and the service with the mvn spring-boot:run command and making sure that Keycloak is running with the settings described in this article. We then call the http://localhost:8090/library/bookOfTheMonth endpoint. A login page will be presented. We will log in using the user and password we gave as credentials to the Kaycloak realm. If the login succeeds, we expect to see the string “bookOfTheMonth”.
Conclusion
OAuth2 is a widely adopted standard in securing web applications. In this article, we have seen the basics of integrating it into a Spring Cloud system using a Keycloak authorization and authentication server. You can find the source code of the example of this article on GitHub.
