The goal of this project is to create a simple Spring Boot REST API, called simple-service
, and secure it with Keycloak
. Furthermore, the API users will be loaded into Keycloak
from OpenLDAP
server.
Note: In
springboot-react-keycloak
repository, we have implemented amovies-app
usingKeycloak
(withPKCE
). This application consists of two services: the backend that was implemented usingSpring Boot
and the frontend implemented withReactJS
.
On ivangfr.github.io, I have compiled my Proof-of-Concepts (PoCs) and articles. You can easily search for the technology you are interested in by using the filter. Who knows, perhaps I have already implemented a PoC or written an article about what you are looking for.
- [Medium] Implementing and Securing a Simple Spring Boot REST API with Keycloak
- [Medium] Implementing and Securing a Simple Spring Boot UI (Thymeleaf + RBAC) with Keycloak
- [Medium] Implementing and Securing a Spring Boot GraphQL API with Keycloak
- [Medium] Setting Up OpenLDAP With Keycloak For User Federation
- [Medium] Integrating GitHub as a Social Identity Provider in Keycloak
- [Medium] Integrating Google as a Social Identity Provider in Keycloak
- [Medium] Building a Single Spring Boot App with Keycloak or Okta as IdP: Introduction
- [Medium] Implementing a Full Stack Web App using Spring-Boot and React
- [Medium] Using Keycloak to secure a Full Stack Web App implemented with Spring-Boot and React
-
Spring Boot
Web Java application that exposes the following endpoints:GET /api/public
: it's a not secured endpoint, everybody can access it;GET /api/private
: it's a secured endpoint, only accessible by users that provide aJWT
access token issued byKeycloak
and the token must contain the roleUSER
;GET /actuator/*
: they are not secured endpoint, used to expose operational information about the application.
-
Open a terminal and inside
springboot-keycloak-openldap
root folder run:docker compose up -d
-
Just wait for the Docker containers to start running. The Keycloak Docker container usually takes longer. You can check its progress by running this command:
docker logs keycloak -f
Press
Ctrl+C
to exitOnce you see the following log, Keycloak has started:
INFO [io.quarkus] (main) Keycloak 25.0.1 on JVM (powered by Quarkus 3.8.5) started in 27.529s. Listening on: http://0.0.0.0:8080. Management interface listening on http://0.0.0.0:9000.
The LDIF
file that we will use, springboot-keycloak-openldap/ldap/ldap-mycompany-com.ldif
, contains a pre-defined structure for mycompany.com
. Basically, it has 2 groups (developers
and admin
) and 4 users (Bill Gates
, Steve Jobs
, Mark Cuban
and Ivan Franchin
). Besides, it's defined that Bill Gates
, Steve Jobs
and Mark Cuban
belong to developers
group and Ivan Franchin
belongs to admin
group.
Bill Gates > username: bgates, password: 123
Steve Jobs > username: sjobs, password: 123
Mark Cuban > username: mcuban, password: 123
Ivan Franchin > username: ifranchin, password: 123
There are two ways to import those users: running a script or using phpldapadmin
website.
-
In a terminal and inside
springboot-keycloak-openldap
root folder run:./import-openldap-users.sh
-
The command below can be used to check the users imported:
ldapsearch -x -D "cn=admin,dc=mycompany,dc=com" \ -w admin -H ldap://localhost:389 \ -b "ou=users,dc=mycompany,dc=com" \ -s sub "(uid=*)"
-
Access https://localhost:6443
-
Login with the credentials:
Login DN: cn=admin,dc=mycompany,dc=com Password: admin
-
Import the file
springboot-keycloak-openldap/ldap/ldap-mycompany-com.ldif
. -
You should see a tree like the one shown in the picture below:
There are two ways: running a script or using Keycloak
website.
-
In a terminal, make sure you are inside
springboot-keycloak-openldap
root folder. -
Run the script below to configure
Keycloak
forsimple-service
application:./init-keycloak.sh
It creates
company-services
realm,simple-service
client,USER
client role,ldap
federation and the usersbgates
andsjobs
with the roleUSER
assigned. -
Copy
SIMPLE_SERVICE_CLIENT_SECRET
value that is shown at the end of the script. It will be needed whenever we callKeycloak
to get aJWT
access token to accesssimple-service
.
Please, have a look at this Medium article, Setting Up OpenLDAP With Keycloak For User Federation
-
Open a new terminal and make sure you are in
springboot-keycloak-openldap
root folder. -
Start the application by running the following command:
./mvnw clean spring-boot:run --projects simple-service -Dspring-boot.run.jvmArguments="-Dserver.port=9080"
-
Open a new terminal.
-
Call the endpoint
GET /api/public
:curl -i http://localhost:9080/api/public
It should return:
HTTP/1.1 200 It is public.
-
Try to call the endpoint
GET /api/private
without authentication:curl -i http://localhost:9080/api/private
It should return:
HTTP/1.1 401
-
Create an environment variable that contains the
Client Secret
generated byKeycloak
tosimple-service
at Configure Keycloak step:SIMPLE_SERVICE_CLIENT_SECRET=...
-
Run the command below to get an access token for
bgates
user:BGATES_ACCESS_TOKEN=$(curl -s -X POST \ "http://localhost:8080/realms/company-services/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=bgates" \ -d "password=123" \ -d "grant_type=password" \ -d "client_secret=$SIMPLE_SERVICE_CLIENT_SECRET" \ -d "client_id=simple-service" | jq -r .access_token)
Note: In jwt.io, you can decode and verify the
JWT
access token -
Call the endpoint
GET /api/private
:curl -i http://localhost:9080/api/private -H "Authorization: Bearer $BGATES_ACCESS_TOKEN"
It should return:
HTTP/1.1 200 bgates, it is private.
-
The access token default expiration period is
5 minutes
. So, wait for this time and, using the same access token, try to call the private endpoint.It should return:
HTTP/1.1 401 WWW-Authenticate: Bearer realm="company-services", error="invalid_token", error_description="Token is not active"
-
Click
GET /api/public
to open it. Then, clickTry it out
button and, finally, clickExecute
button.It should return:
Code: 200 Response Body: It is public.
-
Now click
GET /api/private
secured endpoint. Let's try it without authentication. Then, clickTry it out
button and, finally, clickExecute
button.It should return:
Code: 401 Details: Error: response status is 401
-
In order to access the private endpoint, you need an access token. So, open a terminal.
-
Create an environment variable that contains the
Client Secret
generated byKeycloak
tosimple-service
at Configure Keycloak step:SIMPLE_SERVICE_CLIENT_SECRET=...
-
Run the following commands:
BGATES_ACCESS_TOKEN=$(curl -s -X POST \ "http://localhost:8080/realms/company-services/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=bgates" \ -d "password=123" \ -d "grant_type=password" \ -d "client_secret=$SIMPLE_SERVICE_CLIENT_SECRET" \ -d "client_id=simple-service" | jq -r .access_token) echo $BGATES_ACCESS_TOKEN
-
Copy the token generated and go back to
Swagger
. -
Click
Authorize
button and paste the access token in theValue
field. Then, clickAuthorize
button and, to finalize, clickClose
. -
Go to
GET /api/private
and call this endpoint again, now with authentication.It should return:
Code: 200 Response Body: bgates, it is private.
You can get an access token to simple-service
using client_id
and client_secret
- Access http://localhost:8080;
- Click the dropdown button that contains
Keycloak
and selectcompany-services
; - On the left menu, click
Clients
; - Select
simple-service
client; - In
Settings
tab:- Go to
Capability config
and checkService accounts roles
checkbox; - Click
Save
button;
- Go to
- In
Service account roles
tab:- Click
service-account-simple-service
link present in the info message;"To manage detail and group mappings, click on the username service-account-simple-service"
- In
Role mapping
tab:- Click
Assign role
button; - Click
Filter by realm roles
dropdown button and selectFilter by clients
; - In
Search by role name
typesimple-service
and pressEnter
; - Select
[simple-service] USER
name and clickAssign
button; - Now,
service-account-simple-service
has the roleUSER
ofsimple-service
assigned.
- Click
- Click
-
Open a terminal.
-
Create an environment variable that contains the
Client Secret
generated byKeycloak
tosimple-service
at Configure Keycloak step.SIMPLE_SERVICE_CLIENT_SECRET=...
-
Run the following command:
CLIENT_ACCESS_TOKEN=$(curl -s -X POST \ "http://localhost:8080/realms/company-services/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_secret=$SIMPLE_SERVICE_CLIENT_SECRET" \ -d "client_id=simple-service" | jq -r .access_token)
-
Try to call the endpoint
GET /api/private
:curl -i http://localhost:9080/api/private -H "Authorization: Bearer $CLIENT_ACCESS_TOKEN"
It should return:
HTTP/1.1 200 service-account-simple-service, it is private.
-
In a terminal, make sure you are in
springboot-keycloak-openldap
root folder. -
Build Docker Image:
- JVM
./docker-build.sh
- Native
./docker-build.sh native
Environment Variable Description KEYCLOAK_HOST
Specify host of the Keycloak
to use (defaultlocalhost
)KEYCLOAK_PORT
Specify port of the Keycloak
to use (default8080
) - JVM
-
Run Docker Container:
docker run --rm --name simple-service \ -p 9080:8080 \ -e KEYCLOAK_HOST=keycloak \ --network=springboot-keycloak-openldap_default \ ivanfranchin/simple-service:1.0.0
-
Open a new terminal.
-
Create an environment variable that contains the
Client Secret
generated byKeycloak
tosimple-service
at Configure Keycloak step.SIMPLE_SERVICE_CLIENT_SECRET=...
-
Run the commands below to get an access token for
bgates
user:BGATES_TOKEN=$( docker run -t --rm -e CLIENT_SECRET=$SIMPLE_SERVICE_CLIENT_SECRET --network springboot-keycloak-openldap_default alpine/curl:latest sh -c ' curl -s -X POST http://keycloak:8080/realms/company-services/protocol/openid-connect/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=bgates" \ -d "password=123" \ -d "grant_type=password" \ -d "client_secret=$CLIENT_SECRET" \ -d "client_id=simple-service"') BGATES_ACCESS_TOKEN=$(echo $BGATES_TOKEN | jq -r .access_token)
-
Call the endpoint
GET /api/private
:curl -i http://localhost:9080/api/private -H "Authorization: Bearer $BGATES_ACCESS_TOKEN"
It should return:
HTTP/1.1 200 bgates, it is private.
- To stop
simple-service
application, go to the terminal where it is running and pressCtrl+C
; - To stop and remove docker compose containers, network and volumes, go to a terminal and inside
springboot-keycloak-openldap
root folder, run the following command:docker compose down -v
To remove the Docker image create by this project, go to a terminal and, inside springboot-keycloak-openldap
root folder, run the following script:
./remove-docker-images.sh