Secure Spring Boot 3.1 with Keycloak: Step‑by‑Step Integration Guide

This tutorial shows how to integrate Keycloak 21.1.1 with Spring Boot 3.1 and Spring Security, covering realm and client setup, token acquisition, Maven dependencies, JWT configuration, a protected test endpoint, and verification of authentication behavior.

Programmer DD
Programmer DD
Programmer DD
Secure Spring Boot 3.1 with Keycloak: Step‑by‑Step Integration Guide

Preparation

The tutorial uses Spring Boot 3.1.0 and Keycloak 21.1.1. Other versions may require adjustments.

Configure Keycloak

1. Create a Realm and a Client in Keycloak.

2. Create a SYS_ADMIN role and assign it to a user.

3. Obtain an Access Token via Keycloak’s token endpoint, for example using curl:

curl --location 'http://localhost:9090/realms/MyAppRealm/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=<YOUR_USER_NAME>' \
--data-urlencode 'password=<YOUR_USER_PASSWORD>' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=My-Awesome-App' \
--data-urlencode 'client_secret=<KEYCLOAK_CLIENT_SECRET>' \
--data-urlencode 'scope=openid'

Save the returned Access Token for later use.

Configure Spring Boot Application

1. Create a Spring Boot project (refer to a Spring Boot tutorial if needed).

2. Add the following Maven dependency to pom.xml:

<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

3. Add JWT resource‑server settings to application.yml (or application.properties):

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:9090/realms/MyAppRealm
          jwk-set-uri: http://localhost:9090/realms/MyAppRealm/protocol/openid-connect/certs

4. Create a test controller with a protected endpoint:

@RequestMapping("/test")
@RestController
public class MySuperSecuredController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}

5. Define a SecurityFilterChain bean to map JWT roles to Spring Security authorities:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .authorizeHttpRequests(registry -> registry
                .requestMatchers("/test/**").hasRole("SYS_ADMIN")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2Configurer -> oauth2Configurer.jwt(jwtConfigurer ->
                jwtConfigurer.jwtAuthenticationConverter(jwt -> {
                    Map<String, Collection<String>> realmAccess = jwt.getClaim("realm_access");
                    Collection<String> roles = realmAccess.get("roles");
                    var grantedAuthorities = roles.stream()
                        .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                        .toList();
                    return new JwtAuthenticationToken(jwt, grantedAuthorities);
                })
            ));
        return httpSecurity.build();
    }
}

Verification

Start the Spring Boot application and ensure Keycloak is running.

Send a request to /test/hello:

If the Authorization header is missing, the server returns a 401 error.

If the header contains the previously obtained Access Token, the request succeeds and returns "hello".

Conclusion

Although Keycloak no longer provides dedicated Spring Security adapters, Spring Security’s built‑in OAuth2 and OIDC support makes integration straightforward once you understand how JWT tokens are processed.

JavaSpring BootJWTOAuth2Spring SecurityKeycloak
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.