Secure Spring API Endpoints with KeyCloak & Spring Security 6+

Securing a Spring Boot endpoint with Keycloak and Spring Security 6 involves integrating your Spring Boot application with Keycloak as the identity provider (IdP) using the spring-boot-starter-oauth2-resource-server module.

Below are the key steps:

1. Add Dependencies

Add these to your pom.xml for Maven:

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

2. Configure application.yml or application.properties

Assuming your Keycloak realm is myrealm, client is spring-app, and server is at http://localhost:8080:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8080/realms/myrealm

3. Secure Endpoints with Spring Security

In Spring Security 6, use the new Lambda-style configuration:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
          .authorizeHttpRequests(auth -> auth
              .requestMatchers("/api/public").permitAll()
              .requestMatchers("/api/admin").hasRole("admin") // or hasAuthority("ROLE_admin")
              .anyRequest().authenticated()
          )
          .oauth2ResourceServer(oauth2 -> oauth2
              .jwt()
          );

        return http.build();
    }
}

Note: Role names from Keycloak come as ROLE_ prefixed or need mapping.

4. (Optional) Map Roles Correctly from Keycloak

By default, Keycloak roles are in the realm_access.roles claim. You may need a JwtAuthenticationConverter:

import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
    grantedAuthoritiesConverter.setAuthoritiesClaimName("realm_access.roles");
    grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");

    JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
    jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
    return jwtAuthenticationConverter;
}

Then plug it into the config:

.oauth2ResourceServer(oauth2 -> oauth2
    .jwt(jwt -> jwt
        .jwtAuthenticationConverter(jwtAuthenticationConverter())
    )
);

5.Protect Endpoints

@RestController
@RequestMapping("/api")
public class TestController {

    @GetMapping("/public")
    public String publicEndpoint() {
        return "This is public";
    }

    @GetMapping("/admin")
    public String adminEndpoint() {
        return "This is admin-only";
    }

    @GetMapping("/user")
    public String userEndpoint() {
        return "This is user-accessible";
    }
}

6. Configure Keycloak

In the Keycloak Admin Console:

  • Create a realm: myrealm
  • Create a client: spring-app (client type: public or confidential)
  • Add roles: admin, user
  • Assign roles to users
  • Test by getting an access token via:
curl -X POST http://localhost:8080/realms/myrealm/protocol/openid-connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=spring-app" \
  -d "username=user" \
  -d "password=pass" \
  -d "grant_type=password"

2 thoughts on “Secure Spring API Endpoints with KeyCloak & Spring Security 6+”

Leave a Comment

Your email address will not be published. Required fields are marked *