1. Overview

In this tutorial, we’ll discuss the basics of setting up a Keycloak server and connecting a Spring Boot application to it using Spring Security OAuth2.0.

2. What Is Keycloak?

Keycloak is an open-source Identity and Access Management solution targeted towards modern applications and services.

Keycloak offers features such as Single-Sign-On (SSO), Identity Brokering and Social Login, User Federation, Client Adapters, an Admin Console, and an Account Management Console.

In our tutorial, we’ll use the Admin Console of Keycloak for setting up and connecting to Spring Boot using the Spring Security OAuth2.0.

3. Setting Up a Keycloak Server

In this section, we will set up and configure the Keycloak server.

3.1. Downloading and Installing Keycloak

There are several distributions to choose from. However, in this tutorial, we’ll be using the standalone version.

Let’s download the Keycloak-22.0.3 Standalone server distribution from the official source.

Once we’ve downloaded the Standalone server distribution, we can unzip and start Keycloak from the terminal:

$ unzip keycloak-22.0.3.zip 
$ cd keycloak-22.0.3
$ bin/kc.sh start-dev

After running these commands, Keycloak will be starting its services. Once we see a line containing Keycloak 22.0.3 […] started, we’ll know its start-up is complete.

Now let’s open a browser and visit http://localhost:8080. We’ll be redirected to http://localhost:8080/auth to create an administrative login:

keycloak

Let’s create an initial admin user named initial1 with the password zaq1!QAZ. Upon clicking Create, we’ll see the message User Created.

We can now proceed to the Administrative Console. On the login page, we’ll enter the initial admin user credentials:

keycloak admin console

3.2. Creating a Realm

A successful login will take us to the console and open up the default Master realm for us.

Here we’ll focus on creating a custom realm.

Let’s navigate to the upper left corner to discover the Create realm button:

create realm

On the next screen, let’s add a new realm called SpringBootKeycloak:

create realm name

After clicking the Create button, a new realm will be created and we’ll be redirected to it. All the operations in the next sections will be performed in this new SpringBootKeycloak realm.

3.3. Creating a Client

Now we’ll navigate to the Clients page. As we can see in the image below, Keycloak comes with Clients that are already built-in:

keycloak clients

We still need to add a new client to our application, so we’ll click Create. We’ll call the new Client login-app:**

create client

In the next screen, for the purpose of this tutorial, we’ll leave all the defaults except the Valid Redirect URIs field. This field should contain the application URL(s) that will use this client for authentication:

keycloak redirect uri

Later on, we’ll be creating a Spring Boot Application running at port 8081 that’ll use this client. Hence we’ve used a redirect URL of http://localhost:8081/* above.

3.4. Creating a Role and a User

Keycloak uses Role-Based Access; therefore, each user must have a role.

To do that, we need to navigate to the Realm Roles page:

realm roles

Then we’ll add the user role:

create role

Now we have a role that can be assigned to users, but as there are no users yet, let’s go to the Users page and add one:

create user

We’ll add a user named user1:

create user

Once the user is created, a page with its details will be displayed:

user details

We can now go to the Credentials tab. We’ll be setting the initial password to xsw2@WS:

user pass

Finally, we’ll navigate to the Role Mappings tab. We’ll be assigning the user role to our user1:

assign role

4. Generating Access Tokens With Keycloak’s API

Keycloak provides a REST API for generating and refreshing access tokens. We can easily use this API to create our own login page.

First, we need to acquire an access token from Keycloak by sending a POST request to this URL:

http://localhost:8080/realms/SpringBootKeycloak/protocol/openid-connect/token

The request should have this body in a x-www-form-urlencoded format:

client_id:<your_client_id>
username:<your_username>
password:<your_password>
grant_type:password

In response, we’ll get an access_token and a refresh_token.

The access token should be used in every request to a Keycloak-protected resource by simply placing it in the Authorization header:

headers: {
    'Authorization': 'Bearer' + access_token
}

Once the access token has expired, we can refresh it by sending a POST request to the same URL as above, but containing the refresh token instead of username and password:

{
    'client_id': 'your_client_id',
    'refresh_token': refresh_token_from_previous_request,
    'grant_type': 'refresh_token'
}

Keycloak will respond to this with a new access_token and refresh_token.

5. Creating and Configuring a Spring Boot Application

In this section, we’ll create a Spring Boot application and configure it as an OAuth Client to interact with the Keycloak server.

5.1. Dependencies

We use the Spring Security OAuth2.0 Client to connect to the Keycloak server.

Let’s start by declaring spring-boot-starter-oauth2-client dependency in a Spring Boot application in the pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Also, as we need to use Spring Security with Spring Boot, we must add this dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

In order to delegate the identification control to a Keycloak server, we’ll use the spring-boot-starter-oauth2-resource-server library. It will allow us to validate a JWT token with the Keycloak server. Hence, let’s add it to our pom:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

Now, the Spring Boot application can interact with Keycloak.

5.2. Keycloak Configuration

We consider the Keycloak client as an OAuth Client. So, we need to configure the Spring Boot application to use the OAuth Client.

The ClientRegistration class holds all of the basic information about the client. Spring auto-configuration looks for properties with the schema spring.security.oauth2.client.registration.[registrationId] and registers a client with OAuth 2.0 or OpenID Connect (OIDC).

Let’s configure the client registration configuration:

spring.security.oauth2.client.registration.keycloak.client-id=login-app
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid

The value we specify in client-id matches the client we named in the admin console.

The Spring Boot application needs to interact with an OAuth 2.0 or OIDC provider to handle the actual request logic for different grant types. So, we need to configure the OIDC provider. It can be auto-configured based on property values with the schema spring.security.oauth2.client.provider.[provider name].

Let’s configure the OIDC provider configuration:

spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

As we can recall, we started Keycloak on port 8080, hence the path specified in issuer-uri. This property identifies the base URI for the authorization server. We enter the realm name we created in the Keycloak admin console. Additionally, we can define user-name-attribute as preferred_username so as to populate our controller’s Principal with a proper user.

Finally, let’s add the configuration needed for validating JWT token against our Keycloak server:

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak

5.3. Configuration Class

*We configure HttpSecurity by creating a SecurityFilterChain bean. Also, we need to enable OAuth2 login using http.oauth2Login().*

Let’s create the security configuration:

@Configuration    
@EnableWebSecurity    
class SecurityConfig {    
    private final KeycloakLogoutHandler keycloakLogoutHandler;    
    SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) {    
        this.keycloakLogoutHandler = keycloakLogoutHandler;    
    }    
    @Bean    
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {    
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());    
    }    
    @Order(1)    
    @Bean    
    public SecurityFilterChain clientFilterChain(HttpSecurity http) throws Exception {    
        http.authorizeRequests()    
            .requestMatchers(new AntPathRequestMatcher("/"))    
            .permitAll()    
            .anyRequest()    
            .authenticated();    
        http.oauth2Login()    
            .and()    
            .logout()    
            .addLogoutHandler(keycloakLogoutHandler)    
            .logoutSuccessUrl("/");    
        return http.build();    
    }    
        
    @Order(2)    
    @Bean    
    public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {    
        http.authorizeRequests()    
            .requestMatchers(new AntPathRequestMatcher("/customers*"))    
            .hasRole("USER")    
            .anyRequest()    
            .authenticated();    
        http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);    
        return http.build();    
    }    
    @Bean    
    public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {    
        return http.getSharedObject(AuthenticationManagerBuilder.class)    
            .build();    
    }    
}

In the code above, the oauth2Login() method adds OAuth2LoginAuthenticationFilter to the filter chain. This filter intercepts requests and applies the needed logic for OAuth 2 authentication. The oauth2ResourceServer method will validate the bound JWT token against our Keycloak server.

We configure access based on authorities and roles in the configure() method. These constraints ensure that every request to /customers/* will only be authorized if the one requesting it is an authenticated user with the role USER.

Finally, we need to handle logout from Keycloak. To do this, we add KeycloakLogoutHandler class:

@Component
public class KeycloakLogoutHandler implements LogoutHandler {

    private static final Logger logger = LoggerFactory.getLogger(KeycloakLogoutHandler.class);
    private final RestTemplate restTemplate;

    public KeycloakLogoutHandler(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, 
      Authentication auth) {
        logoutFromKeycloak((OidcUser) auth.getPrincipal());
    }

    private void logoutFromKeycloak(OidcUser user) {
        String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout";
        UriComponentsBuilder builder = UriComponentsBuilder
          .fromUriString(endSessionEndpoint)
          .queryParam("id_token_hint", user.getIdToken().getTokenValue());

        ResponseEntity<String> logoutResponse = restTemplate.getForEntity(
        builder.toUriString(), String.class);
        if (logoutResponse.getStatusCode().is2xxSuccessful()) {
            logger.info("Successfulley logged out from Keycloak");
        } else {
            logger.error("Could not propagate logout to Keycloak");
        }
    }

}

The KeycloakLogoutHandler class implements LogoutHandler class and sends a logout request to the Keycloak.

Now, after we authenticate, we’ll be able to access the internal customers page.

5.4. Thymeleaf Web Pages

We’re using Thymeleaf for our web pages.

We’ve got three pages:

  • external.html – an externally facing web page for the public
  • customers.html – an internally facing page that will have its access restricted to only authenticated users with the role user
  • layout.html – a simple layout, consisting of two fragments, that are used for both the externally facing page and the internally facing page

The code for the Thymeleaf templates is available on Github.

5.5. Controller

The web controller maps the internal and external URLs to the appropriate Thymeleaf templates:

@GetMapping(path = "/")
public String index() {
    return "external";
}
    
@GetMapping(path = "/customers")
public String customers(Principal principal, Model model) {
    addCustomers();
    model.addAttribute("customers", customerDAO.findAll());
    model.addAttribute("username", principal.getName());
    return "customers";
}

For the path /customers, we’re retrieving all customers from a repository and adding the result as an attribute to the Model. Later on, we iterate through the results in Thymeleaf.

To be able to display a username, we’re injecting the Principal as well.

We should note that we’re using customers here just as raw data to display, and nothing more.

6. Demonstration

Now we’re ready to test our application. To run a Spring Boot application, we can start it easily through an IDE, like Spring Tool Suite (STS), or run this command in the terminal:

mvn clean spring-boot:run

On visiting http://localhost:8081 we see:

external Facing Keycloak Page

Now we click customers to enter the intranet, which is the location of sensitive information.

Note that we’ve been redirected to authenticate through Keycloak to see if we’re authorized to view this content:

keycloak userlogin

Once we log in as user1, Keycloak will verify our authorization that we have the user role, and we’ll be redirected to the restricted customers page:

customers page

Now we’ve finished the setup of connecting Spring Boot with Keycloak and demonstrating how it works.

As we can see, Spring Boot seamlessly handled the entire process of calling the Keycloak Authorization Server. We did not have to call the Keycloak API to generate the Access Token ourselves, or even send the Authorization header explicitly in our request for protected resources.

7. Conclusion

In this article, we configured a Keycloak server and used it with a Spring Boot Application.

We also learned how to set up Spring Security and use it in conjunction with Keycloak. A working version of the code shown in this article is available over on Github.