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:
Authentication: Ensure that only authenticated users can access the system.
Authorization: Restrict access to specific data and functionalities based on the user's role (veterinarian, owner, admin).
Token Management: Manage secure tokens for logged-in users to ensure efficient session handling.
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:
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.
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.
Secure Token-Based Authentication: The system needs secure token-based authentication using industry-standard protocols (e.g., OAuth2, OpenID Connect) to provide robust security.
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
Log in to the Keycloak Admin Console (default is
admin
/admin
).Click Add Realm and create a realm called
spring-petclinic
.
1.3 Create a Client for Spring PetClinic
In your
spring-petclinic
realm, navigate to Clients and click Create.Set the Client ID to
spring-petclinic
and Access Type toconfidential
.Set the Root URL to your Spring PetClinic application’s URL, e.g.,
http://localhost:8080
.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
Navigate to Roles and create the following roles:
admin
vet
owner
1.5 Create Users
Go to Users and create users for each role. For example:
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.x
ml
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
Start Keycloak if it’s not already running.
Run the Spring PetClinic Application:
./mvnw spring-boot:run
Access the PetClinic App: Open a browser and navigate to
http://localhost:8080
.
Step 4: Test Authorization
Log in as Admin:
- Navigate to
/admin
and log in using theadm
in
user.
- Navigate to
Log in as Vet:
- Navigate to
/vets
and log in using thev
et
user.
- Navigate to
Log in as Owner:
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
Navigate to Roles and create the following roles:
admin
vet
owner
Create Users
Go to Users and create users for each role. For example:
admin
user with theadmin
role.vet
user with thevet
role.owner
user with theowner
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
Create a New Package:
- Inside
src/main/java/org/springframework/samples/petclinic
, create a new package calledsecurity
.
- Inside
Add the
SecurityConfig.java
Class:- Inside the
security
package, create a fileSecurityConfig.java
:
- Inside the
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
.
- This file should be placed under your web package, for example, in
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
Run the Spring PetClinic application:
./mvnw spring-boot:run
Open the application in a browser at
http://localhost:8080
.Attempt to access a protected route, such as
/vets
or/owners
. You will be redirected to the Keycloak login page.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.