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 the
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..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 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
.
Open a terminal and inside the
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 -f keycloak
Press
Ctrl+C
to exitOnce you see the following log, Keycloak has started:
INFO [io.quarkus] (main) Keycloak 26.1.3 on JVM (powered by Quarkus 3.15.3.1) started in 44.567s. 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 predefined structure for mycompany.com
. Basically, it has 2 groups (developers
and admin
) and 4 users (Bill Gates
, Steve Jobs
, Mark Cuban
and Ivan Franchin
). Additionally, it is 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: by running a script or by using phpLDAPadmin
.
In a terminal and inside the
springboot-keycloak-openldap
root folder run:./import-openldap-users.sh
The command below can be used to check the imported users:
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 the
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 the
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) echo $BGATES_ACCESS_TOKEN
Note: In jwt.io, you can decode and verify the
JWT
access tokenCall 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 for 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; - Make sure the
Filter by clients
is selected in the first dropdown button; - 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
of thesimple-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) echo $CLIENT_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 the
springboot-keycloak-openldap
root folder.Build Docker Image:
- JVM
./build-docker-images.sh
- Native
./build-docker-images.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 the
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 the
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 the springboot-keycloak-openldap
root folder, run the following script:
./remove-docker-images.sh