Spring Boot & Keycloak: Role-Based Authorization
Practical Guide to Securing Services with Keycloak and Spring Security
When building microservices or REST APIs, securing endpoints is often one of the first and most critical concerns. Keycloak, an open-source identity and access management solution, pairs seamlessly with Spring Boot to provide authentication and fine-grained role-based authorization using OAuth2 and JWT tokens.
In this article, you’ll learn how to secure a Spring Boot resource server with Keycloak, configure role-based access, and apply clean, maintainable authorization constraints.
1. Why Use Keycloak with Spring Boot?
Keycloak offers several advantages over rolling your own OAuth2 server or using a simpler approach like basic auth:
✅ Standards-based OAuth2 and OpenID Connect
✅ Centralized authentication for multiple applications
✅ Fine-grained role mapping and access control
✅ Excellent Spring Security support via JWT
In a microservice environment, this allows consistent, scalable security across services.
2. Overview of the Architecture
In this guide, you’ll set up:
- A Keycloak server managing realms, clients, and user roles
- A Spring Boot resource server validating JWTs from Keycloak
- Role-based authorization at the endpoint level
The authentication flow:
- A user logs in through Keycloak.
- Keycloak issues a JWT access token containing user roles.
- The client (e.g., frontend app) calls your Spring Boot API, attaching the token.
- Spring Security validates the token and grants/denies access based on roles.
3. Setting Up Keycloak
First, download and run Keycloak (or use a container):
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:24.0 start-dev
Then:
- Log in to Keycloak Admin Console at
http://localhost:8080/admin. - Create a new realm (e.g.,
demo-realm). - Under Clients, create a client (e.g.,
springboot-api) with:- Client ID:
springboot-api - Client Protocol:
openid-connect - Access Type:
confidential - Enable Service Accounts (if you plan to use client credentials)
- Client ID:
- Under Roles, create roles like
userandadmin. - Create users and assign them roles.
Important: Make sure the client has Standard Flow Enabled and Direct Access Grants Enabled if you plan to get tokens via password grant for testing.
4. Spring Boot Resource Server
Let’s create a Spring Boot project with spring-boot-starter-oauth2-resource-server and spring-boot-starter-security.
Add the dependencies in pom.xml:
<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>
5. Configuring JWT Validation
In application.yml:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8080/realms/demo-realm
Spring Boot will automatically fetch the JWKs from Keycloak’s discovery endpoint and validate tokens.
6. Mapping Roles from JWT
By default, Keycloak includes roles under realm_access.roles. To map them into Spring Security’s authorities, you need a custom converter.
JwtAuthConverter.java:
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class JwtAuthConverter implements Converter<Jwt, Collection<SimpleGrantedAuthority>> {
@Override
public Collection<SimpleGrantedAuthority> convert(Jwt jwt) {
var realmAccess = jwt.getClaimAsMap("realm_access");
if (realmAccess == null || realmAccess.isEmpty()) {
return List.of();
}
var roles = (List<String>) realmAccess.get("roles");
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))
.collect(Collectors.toList());
}
}
SecurityConfig.java:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(new JwtAuthConverter());
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter))
);
return http.build();
}
}
Explanation:
/admin/**requiresadminrole./user/**allowsuseroradmin.- Everything else requires authentication.
7. Example Controller
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@GetMapping("/public")
public String publicEndpoint() {
return "Public endpoint - no authentication required";
}
@GetMapping("/user/hello")
public String userHello() {
return "Hello, user!";
}
@GetMapping("/admin/hello")
public String adminHello() {
return "Hello, admin!";
}
}
8. Testing with an Access Token
Use the Keycloak CLI or REST API to obtain a token:
curl \ -d "client_id=springboot-api" \ -d "username=testuser" \ -d "password=testpassword" \ -d "grant_type=password" \ "http://localhost:8080/realms/demo-realm/protocol/openid-connect/token"
Then call the API:
curl -H "Authorization: Bearer $ACCESS_TOKEN" http://localhost:8081/user/hello
9. Opinions and Best Practices
✅ Explicit Role Mapping
Always define a custom converter to avoid surprises in how roles are resolved.
✅ Least Privilege Principle
Keep your roles minimal and clear. Avoid overloading admin roles with too many permissions.
✅ Centralized Authorization Logic
If your services grow, consider using method-level annotations like @PreAuthorize("hasRole('ADMIN')") rather than only URL patterns. This keeps security close to the business logic.
✅ Token Expiration and Refresh
Remember that access tokens are short-lived. Use refresh tokens on the client side for longer sessions.
✅ Keep Your Keycloak Version Up to Date
Security vulnerabilities do appear; upgrade regularly.
10. Conclusion
Combining Spring Boot with Keycloak offers a powerful, standards-based approach to authentication and role-based authorization in modern applications. You get the best of both worlds: Keycloak’s enterprise-grade identity management and Spring Security’s mature, customizable security filters.
With the example setup above, you now have a robust foundation for securing your services. Feel free to tailor role mappings, authorization rules, and token lifecycles to your needs.





