Blog
Integrate Spring Boot with Keycloak

Overview

As the security needs of modern web applications continue to grow, reliable identity authentication and authorization systems become imperative. In this context, Keycloak's open-source, flexible, and trusted identity management solutions provide a solution to the security challenges. Keycloak makes it easy to work with many applications, not just Spring Boot projects. However, with the deprecation of <i>WebSecurityConfigurerAdapter</i> and the migration of Spring Security to 6.x, things got a bit confusing. But no worries, in this article, I will cover how to integrate Keycloak with Spring Security, using newer versions of both.

I will be using

Keycloak Configuration

Keycloak is an open-source platform (server) designed to centrally manage user identities and access to applications. To use Keycloak, you can download it through this link. But if you have Docker on your PC, I recommend using the docker command to start Keycloak instead of setting up a Keycloak server on the computer.

docker run -p 7080:7080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -v ${HOME}/docker_volumes/keycloak:/opt/keycloak/data quay.io/keycloak/keycloak:26.0.0 start-dev

From the terminal, once you enter this command, it runs a Keycloak container with the specified parameters, making Keycloak accessible at http://localhost:8080]. The admin console can be accessed through [http://localhost:7080/admin*.](http://localhost:8080/admin.)

Your initial username and password will both be “admin” as specified in the command.

1. Creating Realm

After login, you should create a realm:

Realms are the structures that allow an administrator to create isolated groups of applications and users. Keycloak describes the realms as tenants in Keycloak.

The master realm is just to manage Keycloak. Therefore I create my realm:

2. Creating Client

And now we need a client to protect our application. According to the official documentation of Keycloak, clients are applications and services that can request authentication of a user.

To create a new client in PowerRanger, I click the “Clients” button on the left menu and then click on the “Create client” button:

In the next screens, keep the default configurations and save. Now the client was successfully created.

Then we need to provide a Valid Redirect URIs.

The Keycloak server runs on port 8080. So I want to run my Spring Boot application on port 8090. So I give http://localhost:8080/* as redirect URIs and click on the ‘save’ button.

3. Creating Roles

Keycloak provides a convenient way to create roles, assign them to users, and manage application roles effectively. In Keycloak mainly there are two types of roles.

  1. Client Roles: These roles are specific to a particular client application. Clients are created in the Keycloak administration interface and can be assigned to users within a specific client.
  2. Realm Roles: These roles represent a realm, which can be a real or virtual application domain. Realm roles are applicable to all clients within a realm and can be assigned to users across the entire realm.

And in addition to them, there are composite roles. A composite role is not a role type but a special role that includes multiple authorities.

First I create two client roles called user and admin. To do this, I click on the client that I created and then click on “Create role” :

As you can see, they are not composite roles yet, and they don’t need to be composite by default — only when necessary. I will make them composite just for the sake of this example. So I create two realm roles. To do this, navigate to the Realm Roles page to create roles.

I created app_admin and app_user by clicking on the button in the rectangle. And then click on one and on the prompted page click on the right-top dropdown menu and select the “Add associated roles” option:

and on the page that came up, change the filter to “Filter by clients”. You will see your client roles:

4. Creating Users

The last part that needs to be added is the users. I go to the Users page and create three users with these roles:

  • user1 with app_admin role
  • user2 with app_user role
  • user3 with app_user and app_admin roles

I go to the “Users” page and click on “Add user”:

Now I need to set passwords for users. I go to the “Users” page and click on user2 and select the “Credentials” tab on the page that came and then click on “Set password” button:

After saving credentials I click on the “Role mapping” tab:

I set the same password to other users as well, and I assigned the roles I mentioned above to the relevant users.

Up to this point, authentication and authorization processes are complete. Remember to customize your Keycloak account credentials for the safety of the application. You can use the “Manage account” menu to do this:

Now let’s move on to obtaining the token.

When you go to the “Realm settings” section on the sidebar you will see the “OpenID Endpoint Configuration” link:

When you click on this link, you will see a list of available endpoints:

We can send now a post request to this endpoint to get a token. We can use one of the users we created as the credentials. I use postman for this request:

When you configure the settings indicated in the image, you will be able to obtain the access token.

Spring Boot Application Configuration

The Keycloak configuration concludes here. Now, we can proceed to integrate it with our Spring Boot application.

Creating the Spring Boot Project

I’m using Spring Boot version 3.3.4, Java 21 and Maven as the package manager.

After settings, adding necessary dependencies, generating and downloading the project I open it by IntelliJ IDEA and change the port from the default value 8080 on which the project will run, in the application.properties file as Keycloak runs on port 7080.

I am creating now a simple REST API to test the application. I am just adding the class below under the package controller I created.

@Slf4j
@RestController
@RequestMapping("/api")
public class HelloController {
    @GetMapping("/hello")
    public ResponseEntity<String> sayHello() {
        log.info("Hello World");
        return ResponseEntity.ok("Hello");
    }

    @GetMapping("/admin")
    public ResponseEntity<String> sayHelloToAdmin() {
        log.info("Hello Admin");
        return ResponseEntity.ok("Hello Admin");
    }

    @GetMapping("/user")
    public ResponseEntity<String> sayHelloToUser() {
        log.info("Hello User");
        return ResponseEntity.ok("Hello User");
    }

    @GetMapping("/admin-and-user")
    public ResponseEntity<String> sayHelloToAdminOrUser() {
        log.info("Hello Admin or User");
        return ResponseEntity.ok("Hello Admin or User");
    }
}

Keycloak Integration

In the application.properties file, I add the following settings:

spring.application.name=spring-boot-keycloak-sample3
server.port=8080
# Security Configuration
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:7080/realms/PowerRanger
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
# JWT Configuration
jwt.auth.converter.resource-id=black_ranger
jwt.auth.converter.principal-attribute=principal_username
# Logging Configuration
logging.level.org.springframework.security=DEBUG
  • spring.application.name=KeycloakSpringBootApplication: This setting defines the name of the Spring application. It represents the overall name of the application.
  • spring.security.oauth2.resourceserver.jwt.issuer-uri: This setting specifies the Issuer URI for JWT authentication. It includes the URI of the Keycloak realm.
  • spring.security.oauth2.resourceserver.jwt.jwk-set-uri: Specifies the JWK set URI at the location where JWT will be used. This URI is used to fetch public keys.
  • jwt.auth.converter.resource-id: Specifies the resource ID of the JWT. Typically, it includes a client identifier.
  • jwt.auth.converter.principal-attribute: Specifies a specific attribute name in the JWT's header (such as principal_username) where principals (users) are indicated.

Now, I need security to protect my API. To achieve this, I will create a SecurityConfig class using Spring Security. However, as I aim to integrate my project with Keycloak, I initially create two classes to decode the JWT obtained from the Keycloak server.

Under the security package:

@Data
@Validated
@Configuration
@ConfigurationProperties(prefix = "jwt.auth.converter")
public class JwtConverterProperties {

    private String resourceId;
    private String principalAttribute;
}


The JwtConverterProperties class is a configuration class for JWT authentication conversion. It holds properties like “resourceId” (Keycloak resource identifier) and “principalAttribute” (preferred principal attribute). These properties play a crucial role in decoding and processing JWTs within the Keycloak integration.

@Component
public class JwtConverter implements Converter<Jwt, AbstractAuthenticationToken> {

    private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();

    private final JwtConverterProperties properties;

    public JwtConverter(JwtConverterProperties properties) {
        this.properties = properties;
    }

    @Override
    public AbstractAuthenticationToken convert(Jwt jwt) {
        Collection<GrantedAuthority> authorities = Stream.concat(
                jwtGrantedAuthoritiesConverter.convert(jwt).stream(),
                extractResourceRoles(jwt).stream()).collect(Collectors.toSet());
        return new JwtAuthenticationToken(jwt, authorities, getPrincipalClaimName(jwt));
    }

    private String getPrincipalClaimName(Jwt jwt) {
        String claimName = JwtClaimNames.SUB;
        if (properties.getPrincipalAttribute() != null) {
            claimName = properties.getPrincipalAttribute();
        }
        return jwt.getClaim(claimName);
    }

    private Collection<? extends GrantedAuthority> extractResourceRoles(Jwt jwt) {
        Map<String, Object> resourceAccess = jwt.getClaim("resource_access");
        Map<String, Object> resource;
        Collection<String> resourceRoles;

        if (resourceAccess == null
                || (resource = (Map<String, Object>) resourceAccess.get(properties.getResourceId())) == null
                || (resourceRoles = (Collection<String>) resource.get("roles")) == null) {
            return Set.of();
        }
        return resourceRoles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                .collect(Collectors.toSet());
    }
}

JwtConverter decodes JWTs from Keycloak, enabling role-based endpoint access and user information extraction in Spring Security. It implements the Converter interface, extracting roles and details from JWTs, including resource-specific roles. Configurable via JwtConverterProperties, it plays a dual role: access control and JWT deciphering.

And the SecurityConfig class (for more info about new spring security you can follow this link):

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    public static final String ADMIN = "admin";
    public static final String USER = "user";
    private final JwtConverter jwtConverter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authz) ->
                authz.requestMatchers(HttpMethod.GET, "/api/hello").permitAll()
                        .requestMatchers(HttpMethod.GET, "/api/admin/**").hasRole(ADMIN)
                        .requestMatchers(HttpMethod.GET, "/api/user/**").hasRole(USER)
                        .requestMatchers(HttpMethod.GET, "/api/admin-and-user/**").hasAnyRole(ADMIN, USER)
                        .anyRequest().authenticated());

        http.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        http.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter)));

        return http.build();
    }
}

Now the project structure looks like the below:

After Spring application configurations, we can now run our project and test it. I use postman to do this:

I picked up user2 as the credentials. I get the token I’ve achieved and go to the get request:

I set up the authorization type to Bareer Token , pasted the token into the token box, and sent a GET request to the admin endpoint. And it got 403 Forbidden. Because user2 has just the user role not admin.

Now I get a token by the credentials of user1 as he has the admin role and send a request to the same endpoint. The result:

That’s all!

Conclusion

We set up a Keycloak server using the Docker command. We used the -v flag in this command to store the data, such as realms, clients, and users, credentials on the local machine. We covered the concepts of realms and clients in Keycloak, as well as the types of roles available. Users were created and assigned roles. Subsequently, we developed a Spring Boot RESTful API application and integrated it with our Keycloak server. We secured endpoints using tokens obtained from Keycloak.

You can access the Spring Boot project at this link.

Thank you for your time!

Integrate Spring Boot with Keycloak
Nov 28, 2024

Overview

As the security needs of modern web applications continue to grow, reliable identity authentication and authorization systems become imperative. In this context, Keycloak's open-source, flexible, and trusted identity management solutions provide a solution to the security challenges. Keycloak makes it easy to work with many applications, not just Spring Boot projects. However, with the deprecation of <i>WebSecurityConfigurerAdapter</i> and the migration of Spring Security to 6.x, things got a bit confusing. But no worries, in this article, I will cover how to integrate Keycloak with Spring Security, using newer versions of both.

I will be using

Keycloak Configuration

Keycloak is an open-source platform (server) designed to centrally manage user identities and access to applications. To use Keycloak, you can download it through this link. But if you have Docker on your PC, I recommend using the docker command to start Keycloak instead of setting up a Keycloak server on the computer.

docker run -p 7080:7080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -v ${HOME}/docker_volumes/keycloak:/opt/keycloak/data quay.io/keycloak/keycloak:26.0.0 start-dev

From the terminal, once you enter this command, it runs a Keycloak container with the specified parameters, making Keycloak accessible at http://localhost:8080]. The admin console can be accessed through [http://localhost:7080/admin*.](http://localhost:8080/admin.)

Your initial username and password will both be “admin” as specified in the command.

1. Creating Realm

After login, you should create a realm:

Realms are the structures that allow an administrator to create isolated groups of applications and users. Keycloak describes the realms as tenants in Keycloak.

The master realm is just to manage Keycloak. Therefore I create my realm:

2. Creating Client

And now we need a client to protect our application. According to the official documentation of Keycloak, clients are applications and services that can request authentication of a user.

To create a new client in PowerRanger, I click the “Clients” button on the left menu and then click on the “Create client” button:

In the next screens, keep the default configurations and save. Now the client was successfully created.

Then we need to provide a Valid Redirect URIs.

The Keycloak server runs on port 8080. So I want to run my Spring Boot application on port 8090. So I give http://localhost:8080/* as redirect URIs and click on the ‘save’ button.

3. Creating Roles

Keycloak provides a convenient way to create roles, assign them to users, and manage application roles effectively. In Keycloak mainly there are two types of roles.

  1. Client Roles: These roles are specific to a particular client application. Clients are created in the Keycloak administration interface and can be assigned to users within a specific client.
  2. Realm Roles: These roles represent a realm, which can be a real or virtual application domain. Realm roles are applicable to all clients within a realm and can be assigned to users across the entire realm.

And in addition to them, there are composite roles. A composite role is not a role type but a special role that includes multiple authorities.

First I create two client roles called user and admin. To do this, I click on the client that I created and then click on “Create role” :

As you can see, they are not composite roles yet, and they don’t need to be composite by default — only when necessary. I will make them composite just for the sake of this example. So I create two realm roles. To do this, navigate to the Realm Roles page to create roles.

I created app_admin and app_user by clicking on the button in the rectangle. And then click on one and on the prompted page click on the right-top dropdown menu and select the “Add associated roles” option:

and on the page that came up, change the filter to “Filter by clients”. You will see your client roles:

4. Creating Users

The last part that needs to be added is the users. I go to the Users page and create three users with these roles:

  • user1 with app_admin role
  • user2 with app_user role
  • user3 with app_user and app_admin roles

I go to the “Users” page and click on “Add user”:

Now I need to set passwords for users. I go to the “Users” page and click on user2 and select the “Credentials” tab on the page that came and then click on “Set password” button:

After saving credentials I click on the “Role mapping” tab:

I set the same password to other users as well, and I assigned the roles I mentioned above to the relevant users.

Up to this point, authentication and authorization processes are complete. Remember to customize your Keycloak account credentials for the safety of the application. You can use the “Manage account” menu to do this:

Now let’s move on to obtaining the token.

When you go to the “Realm settings” section on the sidebar you will see the “OpenID Endpoint Configuration” link:

When you click on this link, you will see a list of available endpoints:

We can send now a post request to this endpoint to get a token. We can use one of the users we created as the credentials. I use postman for this request:

When you configure the settings indicated in the image, you will be able to obtain the access token.

Spring Boot Application Configuration

The Keycloak configuration concludes here. Now, we can proceed to integrate it with our Spring Boot application.

Creating the Spring Boot Project

I’m using Spring Boot version 3.3.4, Java 21 and Maven as the package manager.

After settings, adding necessary dependencies, generating and downloading the project I open it by IntelliJ IDEA and change the port from the default value 8080 on which the project will run, in the application.properties file as Keycloak runs on port 7080.

I am creating now a simple REST API to test the application. I am just adding the class below under the package controller I created.

@Slf4j
@RestController
@RequestMapping("/api")
public class HelloController {
    @GetMapping("/hello")
    public ResponseEntity<String> sayHello() {
        log.info("Hello World");
        return ResponseEntity.ok("Hello");
    }

    @GetMapping("/admin")
    public ResponseEntity<String> sayHelloToAdmin() {
        log.info("Hello Admin");
        return ResponseEntity.ok("Hello Admin");
    }

    @GetMapping("/user")
    public ResponseEntity<String> sayHelloToUser() {
        log.info("Hello User");
        return ResponseEntity.ok("Hello User");
    }

    @GetMapping("/admin-and-user")
    public ResponseEntity<String> sayHelloToAdminOrUser() {
        log.info("Hello Admin or User");
        return ResponseEntity.ok("Hello Admin or User");
    }
}

Keycloak Integration

In the application.properties file, I add the following settings:

spring.application.name=spring-boot-keycloak-sample3
server.port=8080
# Security Configuration
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:7080/realms/PowerRanger
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
# JWT Configuration
jwt.auth.converter.resource-id=black_ranger
jwt.auth.converter.principal-attribute=principal_username
# Logging Configuration
logging.level.org.springframework.security=DEBUG
  • spring.application.name=KeycloakSpringBootApplication: This setting defines the name of the Spring application. It represents the overall name of the application.
  • spring.security.oauth2.resourceserver.jwt.issuer-uri: This setting specifies the Issuer URI for JWT authentication. It includes the URI of the Keycloak realm.
  • spring.security.oauth2.resourceserver.jwt.jwk-set-uri: Specifies the JWK set URI at the location where JWT will be used. This URI is used to fetch public keys.
  • jwt.auth.converter.resource-id: Specifies the resource ID of the JWT. Typically, it includes a client identifier.
  • jwt.auth.converter.principal-attribute: Specifies a specific attribute name in the JWT's header (such as principal_username) where principals (users) are indicated.

Now, I need security to protect my API. To achieve this, I will create a SecurityConfig class using Spring Security. However, as I aim to integrate my project with Keycloak, I initially create two classes to decode the JWT obtained from the Keycloak server.

Under the security package:

@Data
@Validated
@Configuration
@ConfigurationProperties(prefix = "jwt.auth.converter")
public class JwtConverterProperties {

    private String resourceId;
    private String principalAttribute;
}


The JwtConverterProperties class is a configuration class for JWT authentication conversion. It holds properties like “resourceId” (Keycloak resource identifier) and “principalAttribute” (preferred principal attribute). These properties play a crucial role in decoding and processing JWTs within the Keycloak integration.

@Component
public class JwtConverter implements Converter<Jwt, AbstractAuthenticationToken> {

    private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();

    private final JwtConverterProperties properties;

    public JwtConverter(JwtConverterProperties properties) {
        this.properties = properties;
    }

    @Override
    public AbstractAuthenticationToken convert(Jwt jwt) {
        Collection<GrantedAuthority> authorities = Stream.concat(
                jwtGrantedAuthoritiesConverter.convert(jwt).stream(),
                extractResourceRoles(jwt).stream()).collect(Collectors.toSet());
        return new JwtAuthenticationToken(jwt, authorities, getPrincipalClaimName(jwt));
    }

    private String getPrincipalClaimName(Jwt jwt) {
        String claimName = JwtClaimNames.SUB;
        if (properties.getPrincipalAttribute() != null) {
            claimName = properties.getPrincipalAttribute();
        }
        return jwt.getClaim(claimName);
    }

    private Collection<? extends GrantedAuthority> extractResourceRoles(Jwt jwt) {
        Map<String, Object> resourceAccess = jwt.getClaim("resource_access");
        Map<String, Object> resource;
        Collection<String> resourceRoles;

        if (resourceAccess == null
                || (resource = (Map<String, Object>) resourceAccess.get(properties.getResourceId())) == null
                || (resourceRoles = (Collection<String>) resource.get("roles")) == null) {
            return Set.of();
        }
        return resourceRoles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                .collect(Collectors.toSet());
    }
}

JwtConverter decodes JWTs from Keycloak, enabling role-based endpoint access and user information extraction in Spring Security. It implements the Converter interface, extracting roles and details from JWTs, including resource-specific roles. Configurable via JwtConverterProperties, it plays a dual role: access control and JWT deciphering.

And the SecurityConfig class (for more info about new spring security you can follow this link):

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    public static final String ADMIN = "admin";
    public static final String USER = "user";
    private final JwtConverter jwtConverter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authz) ->
                authz.requestMatchers(HttpMethod.GET, "/api/hello").permitAll()
                        .requestMatchers(HttpMethod.GET, "/api/admin/**").hasRole(ADMIN)
                        .requestMatchers(HttpMethod.GET, "/api/user/**").hasRole(USER)
                        .requestMatchers(HttpMethod.GET, "/api/admin-and-user/**").hasAnyRole(ADMIN, USER)
                        .anyRequest().authenticated());

        http.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        http.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter)));

        return http.build();
    }
}

Now the project structure looks like the below:

After Spring application configurations, we can now run our project and test it. I use postman to do this:

I picked up user2 as the credentials. I get the token I’ve achieved and go to the get request:

I set up the authorization type to Bareer Token , pasted the token into the token box, and sent a GET request to the admin endpoint. And it got 403 Forbidden. Because user2 has just the user role not admin.

Now I get a token by the credentials of user1 as he has the admin role and send a request to the same endpoint. The result:

That’s all!

Conclusion

We set up a Keycloak server using the Docker command. We used the -v flag in this command to store the data, such as realms, clients, and users, credentials on the local machine. We covered the concepts of realms and clients in Keycloak, as well as the types of roles available. Users were created and assigned roles. Subsequently, we developed a Spring Boot RESTful API application and integrated it with our Keycloak server. We secured endpoints using tokens obtained from Keycloak.

You can access the Spring Boot project at this link.

Thank you for your time!

Subscribe To Our Newsletter

Do get in touch with us to understand more about how we can help your organization in building meaningful and in-demand products
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Blog

Integrate Spring Boot with Keycloak

Written by:  

Hendi

November 28, 2024

15 min read

Integrate Spring Boot with Keycloak

Overview

As the security needs of modern web applications continue to grow, reliable identity authentication and authorization systems become imperative. In this context, Keycloak's open-source, flexible, and trusted identity management solutions provide a solution to the security challenges. Keycloak makes it easy to work with many applications, not just Spring Boot projects. However, with the deprecation of <i>WebSecurityConfigurerAdapter</i> and the migration of Spring Security to 6.x, things got a bit confusing. But no worries, in this article, I will cover how to integrate Keycloak with Spring Security, using newer versions of both.

I will be using

Keycloak Configuration

Keycloak is an open-source platform (server) designed to centrally manage user identities and access to applications. To use Keycloak, you can download it through this link. But if you have Docker on your PC, I recommend using the docker command to start Keycloak instead of setting up a Keycloak server on the computer.

docker run -p 7080:7080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -v ${HOME}/docker_volumes/keycloak:/opt/keycloak/data quay.io/keycloak/keycloak:26.0.0 start-dev

From the terminal, once you enter this command, it runs a Keycloak container with the specified parameters, making Keycloak accessible at http://localhost:8080]. The admin console can be accessed through [http://localhost:7080/admin*.](http://localhost:8080/admin.)

Your initial username and password will both be “admin” as specified in the command.

1. Creating Realm

After login, you should create a realm:

Realms are the structures that allow an administrator to create isolated groups of applications and users. Keycloak describes the realms as tenants in Keycloak.

The master realm is just to manage Keycloak. Therefore I create my realm:

2. Creating Client

And now we need a client to protect our application. According to the official documentation of Keycloak, clients are applications and services that can request authentication of a user.

To create a new client in PowerRanger, I click the “Clients” button on the left menu and then click on the “Create client” button:

In the next screens, keep the default configurations and save. Now the client was successfully created.

Then we need to provide a Valid Redirect URIs.

The Keycloak server runs on port 8080. So I want to run my Spring Boot application on port 8090. So I give http://localhost:8080/* as redirect URIs and click on the ‘save’ button.

3. Creating Roles

Keycloak provides a convenient way to create roles, assign them to users, and manage application roles effectively. In Keycloak mainly there are two types of roles.

  1. Client Roles: These roles are specific to a particular client application. Clients are created in the Keycloak administration interface and can be assigned to users within a specific client.
  2. Realm Roles: These roles represent a realm, which can be a real or virtual application domain. Realm roles are applicable to all clients within a realm and can be assigned to users across the entire realm.

And in addition to them, there are composite roles. A composite role is not a role type but a special role that includes multiple authorities.

First I create two client roles called user and admin. To do this, I click on the client that I created and then click on “Create role” :

As you can see, they are not composite roles yet, and they don’t need to be composite by default — only when necessary. I will make them composite just for the sake of this example. So I create two realm roles. To do this, navigate to the Realm Roles page to create roles.

I created app_admin and app_user by clicking on the button in the rectangle. And then click on one and on the prompted page click on the right-top dropdown menu and select the “Add associated roles” option:

and on the page that came up, change the filter to “Filter by clients”. You will see your client roles:

4. Creating Users

The last part that needs to be added is the users. I go to the Users page and create three users with these roles:

  • user1 with app_admin role
  • user2 with app_user role
  • user3 with app_user and app_admin roles

I go to the “Users” page and click on “Add user”:

Now I need to set passwords for users. I go to the “Users” page and click on user2 and select the “Credentials” tab on the page that came and then click on “Set password” button:

After saving credentials I click on the “Role mapping” tab:

I set the same password to other users as well, and I assigned the roles I mentioned above to the relevant users.

Up to this point, authentication and authorization processes are complete. Remember to customize your Keycloak account credentials for the safety of the application. You can use the “Manage account” menu to do this:

Now let’s move on to obtaining the token.

When you go to the “Realm settings” section on the sidebar you will see the “OpenID Endpoint Configuration” link:

When you click on this link, you will see a list of available endpoints:

We can send now a post request to this endpoint to get a token. We can use one of the users we created as the credentials. I use postman for this request:

When you configure the settings indicated in the image, you will be able to obtain the access token.

Spring Boot Application Configuration

The Keycloak configuration concludes here. Now, we can proceed to integrate it with our Spring Boot application.

Creating the Spring Boot Project

I’m using Spring Boot version 3.3.4, Java 21 and Maven as the package manager.

After settings, adding necessary dependencies, generating and downloading the project I open it by IntelliJ IDEA and change the port from the default value 8080 on which the project will run, in the application.properties file as Keycloak runs on port 7080.

I am creating now a simple REST API to test the application. I am just adding the class below under the package controller I created.

@Slf4j
@RestController
@RequestMapping("/api")
public class HelloController {
    @GetMapping("/hello")
    public ResponseEntity<String> sayHello() {
        log.info("Hello World");
        return ResponseEntity.ok("Hello");
    }

    @GetMapping("/admin")
    public ResponseEntity<String> sayHelloToAdmin() {
        log.info("Hello Admin");
        return ResponseEntity.ok("Hello Admin");
    }

    @GetMapping("/user")
    public ResponseEntity<String> sayHelloToUser() {
        log.info("Hello User");
        return ResponseEntity.ok("Hello User");
    }

    @GetMapping("/admin-and-user")
    public ResponseEntity<String> sayHelloToAdminOrUser() {
        log.info("Hello Admin or User");
        return ResponseEntity.ok("Hello Admin or User");
    }
}

Keycloak Integration

In the application.properties file, I add the following settings:

spring.application.name=spring-boot-keycloak-sample3
server.port=8080
# Security Configuration
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:7080/realms/PowerRanger
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
# JWT Configuration
jwt.auth.converter.resource-id=black_ranger
jwt.auth.converter.principal-attribute=principal_username
# Logging Configuration
logging.level.org.springframework.security=DEBUG
  • spring.application.name=KeycloakSpringBootApplication: This setting defines the name of the Spring application. It represents the overall name of the application.
  • spring.security.oauth2.resourceserver.jwt.issuer-uri: This setting specifies the Issuer URI for JWT authentication. It includes the URI of the Keycloak realm.
  • spring.security.oauth2.resourceserver.jwt.jwk-set-uri: Specifies the JWK set URI at the location where JWT will be used. This URI is used to fetch public keys.
  • jwt.auth.converter.resource-id: Specifies the resource ID of the JWT. Typically, it includes a client identifier.
  • jwt.auth.converter.principal-attribute: Specifies a specific attribute name in the JWT's header (such as principal_username) where principals (users) are indicated.

Now, I need security to protect my API. To achieve this, I will create a SecurityConfig class using Spring Security. However, as I aim to integrate my project with Keycloak, I initially create two classes to decode the JWT obtained from the Keycloak server.

Under the security package:

@Data
@Validated
@Configuration
@ConfigurationProperties(prefix = "jwt.auth.converter")
public class JwtConverterProperties {

    private String resourceId;
    private String principalAttribute;
}


The JwtConverterProperties class is a configuration class for JWT authentication conversion. It holds properties like “resourceId” (Keycloak resource identifier) and “principalAttribute” (preferred principal attribute). These properties play a crucial role in decoding and processing JWTs within the Keycloak integration.

@Component
public class JwtConverter implements Converter<Jwt, AbstractAuthenticationToken> {

    private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();

    private final JwtConverterProperties properties;

    public JwtConverter(JwtConverterProperties properties) {
        this.properties = properties;
    }

    @Override
    public AbstractAuthenticationToken convert(Jwt jwt) {
        Collection<GrantedAuthority> authorities = Stream.concat(
                jwtGrantedAuthoritiesConverter.convert(jwt).stream(),
                extractResourceRoles(jwt).stream()).collect(Collectors.toSet());
        return new JwtAuthenticationToken(jwt, authorities, getPrincipalClaimName(jwt));
    }

    private String getPrincipalClaimName(Jwt jwt) {
        String claimName = JwtClaimNames.SUB;
        if (properties.getPrincipalAttribute() != null) {
            claimName = properties.getPrincipalAttribute();
        }
        return jwt.getClaim(claimName);
    }

    private Collection<? extends GrantedAuthority> extractResourceRoles(Jwt jwt) {
        Map<String, Object> resourceAccess = jwt.getClaim("resource_access");
        Map<String, Object> resource;
        Collection<String> resourceRoles;

        if (resourceAccess == null
                || (resource = (Map<String, Object>) resourceAccess.get(properties.getResourceId())) == null
                || (resourceRoles = (Collection<String>) resource.get("roles")) == null) {
            return Set.of();
        }
        return resourceRoles.stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                .collect(Collectors.toSet());
    }
}

JwtConverter decodes JWTs from Keycloak, enabling role-based endpoint access and user information extraction in Spring Security. It implements the Converter interface, extracting roles and details from JWTs, including resource-specific roles. Configurable via JwtConverterProperties, it plays a dual role: access control and JWT deciphering.

And the SecurityConfig class (for more info about new spring security you can follow this link):

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    public static final String ADMIN = "admin";
    public static final String USER = "user";
    private final JwtConverter jwtConverter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authz) ->
                authz.requestMatchers(HttpMethod.GET, "/api/hello").permitAll()
                        .requestMatchers(HttpMethod.GET, "/api/admin/**").hasRole(ADMIN)
                        .requestMatchers(HttpMethod.GET, "/api/user/**").hasRole(USER)
                        .requestMatchers(HttpMethod.GET, "/api/admin-and-user/**").hasAnyRole(ADMIN, USER)
                        .anyRequest().authenticated());

        http.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        http.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter)));

        return http.build();
    }
}

Now the project structure looks like the below:

After Spring application configurations, we can now run our project and test it. I use postman to do this:

I picked up user2 as the credentials. I get the token I’ve achieved and go to the get request:

I set up the authorization type to Bareer Token , pasted the token into the token box, and sent a GET request to the admin endpoint. And it got 403 Forbidden. Because user2 has just the user role not admin.

Now I get a token by the credentials of user1 as he has the admin role and send a request to the same endpoint. The result:

That’s all!

Conclusion

We set up a Keycloak server using the Docker command. We used the -v flag in this command to store the data, such as realms, clients, and users, credentials on the local machine. We covered the concepts of realms and clients in Keycloak, as well as the types of roles available. Users were created and assigned roles. Subsequently, we developed a Spring Boot RESTful API application and integrated it with our Keycloak server. We secured endpoints using tokens obtained from Keycloak.

You can access the Spring Boot project at this link.

Thank you for your time!

About Greyamp

Greyamp is a boutique Management Consulting firm that works with large enterprises to help them on their Digital Transformation journeys, going across the organisation, covering process, people, culture, and technology. Subscribe here to get our latest digital transformation insights.