Keycloak with Spring Boot Applications: Step-by-Step Guide

In this tutorial, you will learn how to secure a Spring Boot application using Keycloak and JWT tokens. We will walk through how to quickly set up a Keycloak server and configure a Spring Boot application to authenticate and authorize users via Keycloak. This comprehensive guide is ideal for developers looking to integrate robust, enterprise-grade authentication into their Spring Boot applications.

Prerequisites

Before starting, ensure you have the following installed:

  • Java 17 or later
  • Maven 3.8+
  • Docker (optional, if you prefer running Keycloak in a container)
  • Keycloak 22 or later (self-hosted or Docker image)
  • Basic understanding of Spring Boot and OAuth2 concepts

Step 1: Setting Up Keycloak

To run this example, we will configure a local Keycloak server with a custom Realm called “ApplicationRealm”.

1.1 Download and Install Keycloak

You can download Keycloak from the official Keycloak website or use Docker to run it quickly:

docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:latest start-dev

In this example, we will use the following Keycloak Configuration:

  • Admin credentials: admin/admin
  • Application Realm: ApplicationRealm
  • Client ID: my-client
  • User: user1/123456 (with role “admin”)

To learn more about using Keycloak with Docker check this tutorial: Keycloak Using Docker and Docker Compose (2025 Edition)

1.2 Configure Keycloak via CLI

You can automate the configuration using the Keycloak Admin CLI (kcadm.sh):

#Authenticate with the Admin Server 
./kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin --password admin

#Create Realm ApplicationRealm 
./kcadm.sh create realms -s realm=ApplicationRealm -s enabled=true -o

#Create User user1 
./kcadm.sh create users -r ApplicationRealm \
  -s username=user1 \
  -s enabled=true \
  -s email="joe@gmail.com" \
  -s emailVerified=true \
  -s firstName=Joe \
  -s lastName=Doe


#Set user1 password 
./kcadm.sh set-password -r ApplicationRealm --username user1 --new-password 123456

#Create Client 
./kcadm.sh create clients -r ApplicationRealm -s clientId=my-client -s bearerOnly="false" -s "redirectUris=[\"http://localhost:8180/*\"]" -s enabled=true -s directAccessGrantsEnabled=true -s clientAuthenticatorType=client-secret -s secret=mysecret

#Create Role customer-manager 
./kcadm.sh create roles -r ApplicationRealm -s name=admin

#Assign Role to user1 
./kcadm.sh add-roles --uusername user1 --rolename admin -r ApplicationRealm

This script will configure your realm, user, client, and roles automatically, saving a lot of time for future deployments. Check that your Keycloak Client and Realms are up and running:

keycloak with spring boot tutorial

Step 2: Configuring the Spring Boot Application

Now let’s set up our Spring Boot application to use Keycloak for authentication.

2.1 Add Dependencies

Add the following dependencies to your 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>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Make sure you’re using the latest Spring Boot version as parent:

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>3.4.5</version>
	<relativePath/> <!-- lookup parent from repository -->
</parent

Step 3: Coding the Spring Boot Application

Let’s create a basic REST API with secured endpoints.

3.1 Create the Controller

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

    @GetMapping("/hello")
    public ResponseEntity<String> sayHello() {
        return ResponseEntity.ok("Hello");
    }

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

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

This controller simply defines three endpoints: one public (/hello), and two secured (/admin and /user).

3.2 Set Up the Security Configuration

Here is the SecurityFilterChain which authorizes the REST Endpoints:

@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();
    }

}

To complete the application, we also need a Class JwtConverter which implements Converter . You can find the full source code at the end of this article.


Step 4: Configuring Keycloak Integration

Now configure your application.properties to point to the Keycloak server:

server.port=8180

spring.application.name=KeycloakSpringBootApplication

# Security Configuration
# Specifies the URI of the token issuer (Keycloak realm) for validating JWT tokens.
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/ApplicationRealm

# Specifies the URI to retrieve the JSON Web Key Set (JWKS) from the issuer, used to verify the JWT signature.
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs

# JWT Configuration
# Defines the resource ID that the JWT auth converter should validate against.
jwt.auth.converter.resource-id=my-client

# Specifies the attribute in the JWT that contains the principal (user identifier).
jwt.auth.converter.principal-attribute=principal_username


# Logging Configuration
logging.level.org.springframework.security=DEBUG

Note: Adjust the principal-attribute depending on what claim you want to use (like sub, email, or preferred_username).


Step 5: Testing the Application

5.1 Start Your Spring Boot Application

Use Maven to build and run the application:

mvn clean install spring-boot:run

The application will be available at http://localhost:8180.

5.2 Authenticate and Call the API

Here’s a sample Java client to retrieve a token and access a secured endpoint:

public class SimplePostRequestSecret {

    public static void main(String[] args) {
        String tokenUrl = "http://localhost:8080/realms/ApplicationRealm/protocol/openid-connect/token";
        String apiUrl = "http://localhost:8180/api/admin";

        String grantType = "password";
        String clientId = "my-client";
        String clientSecret = "mysecret"; // Keycloak client secret
        String username = "user1";  // only user1 authorized
        String password = "123456";

        try {
            // Prepare form data
            Map<Object, Object> data = new HashMap<>();
            data.put("grant_type", grantType);
            data.put("client_id", clientId);
            data.put("client_secret", clientSecret); // Add client secret to the form data
            data.put("username", username);
            data.put("password", password);

            String form = data.entrySet()
                    .stream()
                    .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8))
                    .collect(Collectors.joining("&"));

            // Create HttpClient
            HttpClient client = HttpClient.newHttpClient();

            // Create HttpRequest for POST to get the token
            HttpRequest tokenRequest = HttpRequest.newBuilder()
                    .uri(URI.create(tokenUrl))
                    .header("Content-Type", "application/x-www-form-urlencoded")
                    .POST(HttpRequest.BodyPublishers.ofString(form))
                    .build();

            // Send the POST request
            HttpResponse<String> tokenResponse = client.send(tokenRequest, HttpResponse.BodyHandlers.ofString());
            System.out.println("POST Response Code :: " + tokenResponse.statusCode());

            if (tokenResponse.statusCode() == 200) { // success
                String responseBody = tokenResponse.body();
                System.out.println("Response Body: " + responseBody);

                // Parse JSON response
                ObjectMapper mapper = new ObjectMapper();
                JsonNode rootNode = mapper.readTree(responseBody);
                JsonNode accessTokenNode = rootNode.path("access_token");
                String token = accessTokenNode.asText();

                if (!accessTokenNode.isMissingNode()) {
                    System.out.println("Access Token: " + token);

                    // Create HttpRequest for GET to access the secured endpoint
                    HttpRequest apiRequest = HttpRequest.newBuilder()
                            .uri(URI.create(apiUrl))
                            .header("Authorization", "Bearer " + token)
                            .GET()
                            .build();

                    // Send the GET request
                    HttpResponse<String> apiResponse = client.send(apiRequest, HttpResponse.BodyHandlers.ofString());
                    System.out.println("GET Response Code :: " + apiResponse.statusCode());

                    if (apiResponse.statusCode() == 200) { // success
                        System.out.println("GET Response: " + apiResponse.body());
                    } else {
                        System.out.println("GET request not worked");
                        System.out.println("Error Response: " + apiResponse.body());
                    }
                } else {
                    System.out.println("Response: " + responseBody);
                }
            } else {
                System.out.println("POST request not worked");
                System.out.println("Error Response: " + tokenResponse.body());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

This HTTP client will:

  1. Authenticate the user via Keycloak and retrieve a JWT token.
  2. Access the /api/admin endpoint using the token.

You can also use Postman or cURL to test the endpoints manually.


Conclusion

In this guide, you learned how to:

  • Set up a Keycloak server.
  • Secure a Spring Boot REST API using JWT tokens and OAuth2.
  • Configure roles and permissions inside Keycloak.
  • Protect different API endpoints based on user roles.

Integrating Keycloak with Spring Boot provides a powerful and flexible way to manage authentication and authorization for your applications. Whether for internal tools or production-grade microservices, this setup can easily scale to meet your security needs.

Source code: https://github.com/fmarchioni/mastertheboss/tree/master/spring/keycloak


Recommended Articles

Connect Your Node.js App to Keycloak Using the Adapter - A Comprehensive Guide

Learn how to securely connect your Node.js app to Keycloak for authentication and authorization. Includes setup steps and example code.

Enable Two-Factor Authentication (2FA) with Keycloak Using Java

Learn how to integrate 2FA in Keycloak's standard authentication flow for enhanced security. #Keycloak #Java #Authentication #Security

Install Keycloak Client Adapters on WildFly for Enhanced Authentication/Authorization

Learn how to install Keycloak Client Adapters on WildFly 21 for seamless authentication/authorization management. #Keycloak #WildFly #Java

Keycloak Tutorial: Setting Up and Configuring WildFly with the Latest Stable Build

Learn how to set up and configure Keycloak for an Enterprise application running on WildFly. Includes steps for downloading, starting, and configuring a Keycloak Realm.