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.
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.
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 a JWT
access token issued by Keycloak
and the token must contain the role USER
;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
Wait for Docker containers to be up and running. To check it, run
docker compose ps
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
for simple-service
application
./init-keycloak.sh
It creates company-services
realm, simple-service
client, USER
client role, ldap
federation and the users bgates
and sjobs
with the role USER
assigned.
Copy SIMPLE_SERVICE_CLIENT_SECRET
value that is shown at the end of the script. It will be needed whenever we call Keycloak
to get a JWT
access token to access simple-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 by Keycloak
to simple-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, click Try it out
button and, finally, click Execute
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, click Try it out
button and, finally, click Execute
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 by Keycloak
to simple-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 the Value
field. Then, click Authorize
button and, to finalize, click Close
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
Keycloak
and select company-services
Clients
simple-service
clientSettings
tab
Capability config
and check Service accounts roles
checkboxSave
buttonService account roles
tab
service-account-simple-service
link present in the info message
"To manage detail and group mappings, click on the username service-account-simple-service"
Role mapping
tab
Assign role
buttonFilter by realm roles
dropdown button and select Filter by clients
Search by role name
type simple-service
and press Enter
[simple-service] USER
name and click Assign
buttonservice-account-simple-service
has the role USER
of simple-service
assignedOpen a terminal
Create an environment variable that contains the Client Secret
generated by Keycloak
to simple-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
./docker-build.sh
./docker-build.sh native
Environment Variable | Description |
---|---|
KEYCLOAK_HOST |
Specify host of the Keycloak to use (default localhost ) |
KEYCLOAK_PORT |
Specify port of the Keycloak to use (default 8080 ) |
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 by Keycloak
to simple-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.
simple-service
application, go to the terminal where it is running and press Ctrl+C
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