Authors Top

If you have a few years of experience in the Java ecosystem, and you’d like to share that with the community, have a look at our Contribution Guidelines.

Security Top – Temp

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security 5:

>> CHECK OUT THE COURSE
Frontegg – Security – Text1
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

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.

Further reading:

Spring Security and OpenID Connect

Learn how to set up OpenID Connect (from Google) with a simple Spring Security application.

Simple Single Sign-On with Spring Security OAuth2

A simple SSO implementation using Spring Security 5 and Boot.

CAS SSO With Spring Security

Learn how to integrate the Central Authentication Service (CAS) with Spring Security.

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-19.0.1 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-legacy-19.0.1.zip 
$ cd keycloak-legacy-19.0.1/keycloak-19.0.1/bin
$ ./standalone.sh -Djboss.socket.binding.port-offset=100

After running ./standalone.sh, Keycloak will be starting its services. Once we see a line containing Keycloak 19.0.1 (WildFly Core 18.1.1.Final) started, we'll know its start-up is complete.

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

 

keycloak1

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 Add realm button:

keycloak add realm

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

keycloak new realm

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:

keycloak add client 1

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 valid redirect uris

Later on, we'll be creating a Spring Boot Application running at the 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 Roles page:

keycloak roles

Then we'll add the user role:

keycloak add role

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

keycloak users

We'll add a user named user1:

keycloak adduser

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

keycloak user

We can now go to the Credentials tab. We'll be setting the initial password to [email protected]:

keycloak credentials

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

keycloak userrole

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:8180/auth/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>

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:8180/auth/realms/SpringBootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

As we can recall, we started Keycloak on port 8180, 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.

5.3. Configuration Class

We extend the WebSecurityConfigurerAdapter and override the configure(HttpSecurity http) method. Also, we need to enable OAuth2 login using http.oauth2Login().

Let's create the security configuration:

@Configuration
@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final KeycloakLogoutHandler keycloakLogoutHandler;

    SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) {
        this.keycloakLogoutHandler = keycloakLogoutHandler;
    }

    @Bean
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
          .antMatchers("/customers*", "/users*")
          .hasRole("USER")
          .anyRequest()
          .permitAll();
        http.oauth2Login()
           .and()
           .logout()
           .addLogoutHandler(keycloakLogoutHandler)
           .logoutSuccessUrl("/");
    }

}

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.

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 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's 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 customer 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:

externalFacingKeycloakPage e1551248465138

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 set up 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.

Next we'll be reviewing how to use Spring Security in conjunction with our existing application.

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.

Security bottom

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security 5:

>> CHECK OUT THE COURSE
Security footer banner
3 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Comments are closed on this article!