Secure Your Spring Application: Keycloak & OAuth2 Integration and Configuration with Spring PetClinic

Secure Your Spring Application: Keycloak & OAuth2 Integration and Configuration with Spring PetClinic

Video Link: https://youtu.be/cLSTSznrg14

Secure User Authentication and Role-Based Access Control for a Veterinary Clinic Management System

Business Problem:

A veterinary clinic management system, similar to the Spring PetClinic application, requires secure user authentication and role-based access control (RBAC) to ensure that different users (e.g., veterinarians, pet owners, and administrators) can access the system based on their roles. The system manages sensitive information, such as medical records, owner details, and billing data, and requires the following:

  1. Authentication: Ensure that only authenticated users can access the system.

  2. Authorization: Restrict access to specific data and functionalities based on the user's role (veterinarian, owner, admin).

  3. Token Management: Manage secure tokens for logged-in users to ensure efficient session handling.

  4. Seamless Integration: The solution must integrate seamlessly with the existing system without significant rework.

The veterinary clinic seeks a scalable and secure authentication and authorization system to prevent unauthorized access and protect sensitive data.


Challenges:

  1. Implementing Role-Based Access Control (RBAC): The clinic requires the ability to control which roles can access which resources. For instance:

    • Veterinarians should have access to manage pet medical records.

    • Owners should only access their pets' details.

    • Administrators should have full access to manage clinic operations.

  2. Seamless Integration with Existing Systems: The clinic management system is built using Spring Boot, and any integration must fit within the existing architecture with minimal disruption.

  3. Secure Token-Based Authentication: The system needs secure token-based authentication using industry-standard protocols (e.g., OAuth2, OpenID Connect) to provide robust security.

  4. Centralized User Management: The clinic desires centralized identity management to handle user credentials, roles, and permissions efficiently. It must support Single Sign-On (SSO) for both web and mobile clients in the future.


Solution: Integration of Keycloak with Spring PetClinic

Keycloak is an open-source Identity and Access Management solution that solves these authentication and authorization challenges. Keycloak provides a comprehensive framework for managing users, roles, tokens, and Single Sign-On (SSO).

We implemented Keycloak as the authentication and authorization provider for the Spring PetClinic application to meet the clinic’s security requirements.

Flow Diagram

+-------------+                     +--------------+                      +--------------+
|   Browser   | ---- Login ---->     |  Keycloak    |  ---- Redirect ----> | Spring App   |
|             | <--- Auth Token ---  |              |  <--- Token -------> |              |
+-------------+                     +--------------+                      +--------------+

Prerequisites:

  • Java 11 or later

  • Maven

  • A running Keycloak instance (you can run it locally or in Docker)

  • The Spring PetClinic application source code (available on GitHub)

Step 1: Set Up Keycloak

Install Keycloak:

Run the below shell script inside the Ubuntu machine to install keycloak

#!/bin/bash

# Objective: To install Keycloak in Ubuntu Machine
# Date: 24 SEP 2024
# Author: PRAFUL PATEL
# Web: https://www.praful.cloud
# ---------------------------------------------------------------

# Variables (modify these as per your requirements)
KEYCLOAK_VERSION="25.0.6"  # Latest stable version
KEYCLOAK_USER="keycloak"
KEYCLOAK_HOME="/opt/keycloak"
DB_USER="keycloak"
DB_PASSWORD="admin"
DB_NAME="keycloak"
ADMIN_USERNAME="admin"
ADMIN_PASSWORD="admin"

# Step 1: Update system and install required packages
echo "Updating system and installing required packages..."
sudo apt update -y
sudo apt upgrade -y
sudo apt install -y openjdk-17-jdk curl wget unzip

# Step 2: Download Keycloak
echo "Downloading Keycloak..."
wget https://github.com/keycloak/keycloak/releases/download/${KEYCLOAK_VERSION}/keycloak-${KEYCLOAK_VERSION}.zip -P /tmp

# Step 3: Install Keycloak
echo "Installing Keycloak..."
sudo unzip /tmp/keycloak-${KEYCLOAK_VERSION}.zip -d /tmp/
sudo cp -r /tmp/keycloak-${KEYCLOAK_VERSION}/* ${KEYCLOAK_HOME}/


# Step 4: Create Keycloak user and set permissions
echo "Creating Keycloak user and setting permissions..."
sudo useradd -r -d ${KEYCLOAK_HOME} -s /bin/false ${KEYCLOAK_USER}
sudo chown -R ${KEYCLOAK_USER}:${KEYCLOAK_USER} ${KEYCLOAK_HOME}
sudo chmod +x ${KEYCLOAK_HOME}/bin/kc.sh
sudo chmod -R 755 ${KEYCLOAK_HOME}

# Step 5: Set up database (Optional)
echo "Setting up PostgreSQL database..."
sudo apt install -y postgresql postgresql-contrib

sudo -u postgres psql -c "CREATE DATABASE keycloak;"
sudo -u postgres psql -c "CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}';"
sudo -u postgres psql -c "CREATE DATABASE ${DB_NAME};"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};"
sudo -u postgres psql -c  "ALTER DATABASE keycloak OWNER TO keycloak;"


# Step 6: Configure Keycloak to use PostgreSQL
echo "Configuring Keycloak to use PostgreSQL..."
sudo tee ${KEYCLOAK_HOME}/conf/keycloak.conf <<EOF
db=postgres
db-url=jdbc:postgresql://localhost:5432/${DB_NAME}
db-username=${DB_USER}
db-password=${DB_PASSWORD}
EOF

# Step 7: Configure Keycloak as a service
echo "Configuring Keycloak as a service..."
sudo tee /etc/systemd/system/keycloak.service <<EOF
[Unit]
Description=Keycloak Server
After=network.target

[Service]
User=keycloak
Group=keycloak
Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64"
Environment="KEYCLOAK_ADMIN=admin"
Environment="KEYCLOAK_ADMIN_PASSWORD=admin"
ExecStart=/opt/keycloak/bin/kc.sh start --http-port=8081 
WorkingDirectory=/opt/keycloak
Restart=on-failure
LimitNOFILE=102642
TimeoutStopSec=120
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

EOF

# Step 8: Reload systemd, start, and enable Keycloak service
echo "Enabling and starting Keycloak service..."
sudo systemctl daemon-reload
sudo systemctl enable keycloak
sudo systemctl start keycloak

 # Start the keucloak in dev environment
sudo /opt/keycloak/bin/kc.sh start-dev --http-port=8081 --verbose


# Step 9: Display Keycloak Admin Console Information
echo "Installation complete!"
echo "Access Keycloak Admin Console at: http://localhost:8081"
echo "Admin Username: ${ADMIN_USERNAME}"
echo "Admin Password: ${ADMIN_PASSWORD}"

1.1 Install Keycloak (if not already installed)

You can run Keycloak locally or using Docker:

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

This will start Keycloak in development mode. Access Keycloak at http://localhost:8080.

1.2 Create a Realm

  1. Log in to the Keycloak Admin Console (default is admin/admin).

  2. Click Add Realm and create a realm called spring-petclinic.

1.3 Create a Client for Spring PetClinic

  1. In your spring-petclinic realm, navigate to Clients and click Create.

  2. Set the Client ID to spring-petclinic and Access Type to confidential.

  3. Set the Root URL to your Spring PetClinic application’s URL, e.g., http://localhost:8080.

  4. After creating the client, go to the Credentials tab and copy the Client Secret. You will need this for the Spring Boot application configuration.

1.4 Create Roles

  1. Navigate to Roles and create the following roles:

    • admin

    • vet

    • owner

1.5 Create Users

  1. Go to Users and create users for each role. For example:

  2. Assign the corresponding role to each user in the Role Mappings tab.

Step 2: Configure Spring PetClinic to Use Keycloak

You need to modify the Spring PetClinic application to authenticate with Keycloak using the Spring Security Keycloak Adapter.

2.1 Add Keycloak Dependencies to pom.xml

In the pom.xml file, add the necessary Keycloak dependencies:

xmlCopy code<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>21.0.1</version> <!-- Use the latest Keycloak version -->
</dependency>

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-security-adapter</artifactId>
    <version>21.0.1</version>
</dependency>

2.2 Configure Keycloak in application.properties or application.yml

If you use application.properties, add the following:

propertiesCopy codekeycloak.realm=spring-petclinic
keycloak.auth-server-url=http://localhost:8080
keycloak.resource=spring-petclinic
keycloak.credentials.secret=YOUR_CLIENT_SECRET
keycloak.ssl-required=none
keycloak.public-client=false
keycloak.principal-attribute=preferred_username
keycloak.bearer-only=false

If you use application.yml, add the following:

yamlCopy codekeycloak:
  realm: spring-petclinic
  auth-server-url: http://localhost:8080
  resource: spring-petclinic
  credentials:
    secret: YOUR_CLIENT_SECRET
  ssl-required: none
  public-client: false
  principal-attribute: preferred_username
  bearer-only: false

Replace YOUR_CLIENT_SECRET with the client secret you copied from the Keycloak client settings earlier.

2.3 Configure Spring Security

Create a security configuration class that extends KeycloakWebSecurityConfigurerAdapter to secure the endpoints and map roles:

javaCopy codeimport org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
            .authorizeRequests()
            .antMatchers("/vets/**").hasRole("vet")
            .antMatchers("/owners/**").hasRole("owner")
            .antMatchers("/admin/**").hasRole("admin")
            .anyRequest().permitAll();
    }
}

Step 3: Test the Application

  1. Start Keycloak if it’s not already running.

  2. Run the Spring PetClinic Application:

     ./mvnw spring-boot:run
    
  3. Access the PetClinic App: Open a browser and navigate to http://localhost:8080.

    • You will be redirected to the Keycloak login page when you try to access protected routes like /vets, /owners, or /admin.

    • Log in using the corresponding role-based users you created in Keycloak.

Step 4: Test Authorization

  • Log in as Admin:

    • Navigate to /admin and log in using the admin user.
  • Log in as Vet:

    • Navigate to /vets and log in using the vet user.
  • Log in as Owner:

    • Navigate to /owners and log in using the owner user.

Conclusion

By following these steps, you have integrated Keycloak as the authentication and authorization provider for the Spring PetClinic application. The application now authenticates users via Keycloak and restricts access based on user roles.

Create Realm

Create Client

Create Credentials

Copy Client Secret

J4Hl5BSJYfbOAqTYmaFHL6T0MEX

Create Role

Create Roles

  1. Navigate to Roles and create the following roles:

    • admin

    • vet

    • owner

Create Users

  1. Go to Users and create users for each role. For example:

    • admin user with the admin role.

    • vet user with the vet role.

    • owner user with the owner role.

Role mapping

Login

Error

To Fix this error will have to integrate token controller' in spring petclinic app

To handle the Keycloak token in your Spring PetClinic application, you need to add the token management code in the appropriate parts of the Spring Boot application based on how you want to retrieve and use the token.

Here’s where and how you should add the necessary code in the Spring PetClinic application:

Configure Security in Spring Boot

  1. Create a New Package:

    • Inside src/main/java/org/springframework/samples/petclinic, create a new package called security.
  2. Add the SecurityConfig.java Class:

Application level changes to Sprint-Petclinic Application code

Configure Security in Spring Boot

add a new package ‘security’ into java/org/springframworks/samples/petclinic
file - SecurityConfig.java

Create a New Controller to Retrieve the Token

You can create a new controller class in your Spring PetClinic application that retrieves the Keycloak token from the current session using Spring Security’s SecurityContextHolder.

Steps:

  • Create a new controller file in the appropriate package (usually under org.springframework.samples.petclinic.web).

Example: TokenController.java

javaCopy codepackage org.springframework.samples.petclinic.web;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TokenController {

    // This endpoint returns the Keycloak access token for the logged-in user
    @GetMapping("/token")
    public String getToken() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        // Check if the authenticated user is an OIDC user (Keycloak)
        if (authentication.getPrincipal() instanceof OidcUser) {
            OidcUser oidcUser = (OidcUser) authentication.getPrincipal();
            String token = oidcUser.getIdToken().getTokenValue(); // Retrieve the token
            return "Token: " + token;
        }

        return "No token available";
    }
}
  • Where to put this file:

    • This file should be placed under your web package, for example, in src/main/java/org/springframework/samples/petclinic/web/TokenController.java.

Update application properties for keycloak and OAuth2

# database init, supports mysql too
database=h2
spring.sql.init.schema-locations=classpath*:db/${database}/schema.sql
spring.sql.init.data-locations=classpath*:db/${database}/data.sql

# Web
spring.thymeleaf.mode=HTML

# JPA
spring.jpa.hibernate.ddl-auto=none
spring.jpa.open-in-view=false

# Internationalization
spring.messages.basename=messages/messages

# Actuator
management.endpoints.web.exposure.include=*

# Logging
logging.level.org.springframework=INFO
# logging.level.org.springframework.web=DEBUG
# logging.level.org.springframework.context.annotation=TRACE

# Maximum time static resources should be cached
spring.web.resources.cache.cachecontrol.max-age=12h


# Keycloak config
keycloak.realm=spring-petclinic
keycloak.auth-server-url=http://localhost:8081
keycloak.resource=spring-petclinic
keycloak.credentials.secret=6HGIBPgV4yCtIvNDJFaWx110ddNNZwEg
keycloak.ssl-required=none
keycloak.public-client=false
keycloak.principal-attribute=preferred_username
keycloak.bearer-only=false
logging.level.org.springframework.security=DEBUG
logging.level.org.keycloak=DEBUG

# OAuth2 settings for Spring Security (OpenID Connect)
spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8081/realms/spring-petclinic
spring.security.oauth2.client.registration.keycloak.client-id=spring-petclinic
spring.security.oauth2.client.registration.keycloak.client-secret=6HGIBPgV4yCtIvNDJFaWx110ddNNZwEg
spring.security.oauth2.client.registration.keycloak.scope=openid,profile,email
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code

Add Keycloak Dependencies to pom.xml:

    • Open the pom.xml file and add the following dependencies:

         xmlCopy code<dependencies>
           <!-- Keycloak Integration -->
           <dependency>
             <groupId>org.keycloak</groupId>
             <artifactId>keycloak-spring-boot-starter</artifactId>
             <version>21.1.1</version>
           </dependency>
      
           <!-- Spring Security -->
           <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-security</artifactId>
           </dependency>
      
           <dependency>
             <groupId>org.springframework.security</groupId>
             <artifactId>spring-security-config</artifactId>
           </dependency>
      
           <dependency>
             <groupId>org.springframework.security</groupId>
             <artifactId>spring-security-web</artifactId>
           </dependency>
         </dependencies>
      


Add Keycloak Configuration to application.yml

Create or update the application.yml file to include Keycloak integration settings:

yamlCopy codekeycloak:
  realm: spring-petclinic
  auth-server-url: http://localhost:8080/auth
  ssl-required: external
  resource: spring-petclinic
  credentials:
    secret: YOUR_CLIENT_SECRET_HERE
  principal-attribute: preferred_username
  use-resource-role-mappings: true

spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: spring-petclinic
            client-secret: YOUR_CLIENT_SECRET_HERE
            authorization-grant-type: authorization_code
            scope: openid, profile, email
        provider:
          keycloak:
            issuer-uri: http://localhost:8080/auth/realms/spring-petclinic

Run the Application

  1. Run the Spring PetClinic application:

     ./mvnw spring-boot:run
    
  2. Open the application in a browser at http://localhost:8080.

  3. Attempt to access a protected route, such as /vets or /owners. You will be redirected to the Keycloak login page.

  4. Log in with the corresponding Keycloak user, and you will be able to access the route based on the role assigned.


Test Token Retrieval

Access the /token endpoint to retrieve the Keycloak token:

curl http://localhost:8080/token

If authenticated, the token should be returned in the response

Test the Spring Petclinic Application with Keycloak

Login Successful with Keycloak OAuth2

Verify token

Conclusion

Integrating Keycloak with the Spring PetClinic application successfully addressed the need for secure, role-based access control and centralized identity management. By implementing token-based authentication with role mapping, we ensured that sensitive veterinary records are protected, while allowing different user groups (vets, owners, admins) to access only the data they are authorized to manage.

This solution provides the clinic with a scalable, secure, and centralized approach to identity and access management, supporting its long-term growth and security needs.

For further details on integrating Keycloak with Spring PetClinic, visit praful.cloud.