The goals of this project are:
- Create a
Spring Boot
application that manages books, calledbook-service
; - Use
Keycloak
as OpenID Connect Provider; - Test using
Testcontainers
; - Explore the utilities and annotations that
Spring Boot
provides when testing applications.
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] Building a Single Spring Boot App with Keycloak or Okta as IdP: Introduction
-
Spring Boot
Web application that manages books.MongoDB
is used as storage, and the application's sensitive endpoints (like create, update and delete books) are secured.
Open a terminal and, inside springboot-keycloak-mongodb-testcontainers
root folder, run the script below
./init-environment.sh
There are two ways: running a script or using Keycloak
website
-
In a terminal, make sure you are in
springboot-keycloak-mongodb-testcontainers
root folder -
Run the following script to configure
Keycloak
forbook-service
application./init-keycloak.sh
This script creates:
company-services
realm;book-service
client;manage_books
client role;- user with username
ivan.franchin
and password123
and with the rolemanage_books
assigned.
-
The
book-service
client secret (BOOK_SERVICE_CLIENT_SECRET
) is shown at the end of the execution. It will be used in the next step -
You can check the configuration in
Keycloak
by accessing http://localhost:8080. The credentials areadmin/admin
.
-
Access http://localhost:8080
-
Login with the credentials
Username: admin Password: admin
- On the left menu, click the dropdown button that contains
Keycloak
and then, clickCreate Realm
button - Set
company-services
to theRealm name
field and clickCreate
button
- On the left menu, click
Authentication
- Select
Required actions
tab - Disable
Verify Profile
- On the left menu, click
Clients
- Click
Create client
button - In
General Settings
- Set
book-service
toClient ID
- Click
Next
button
- Set
- In
Capability config
- Enable
Client authentication
toggle switch - Click
Next
button
- Enable
- In
Login settings
tab- Set
http://localhost:9080/*
toValid redirect URIs
- Click
Save
button
- Set
- In
Credentials
tab, you can find the secret generated forbook-service
- In
Roles
tab- Click
Create role
button - Set
manage_books
toRole Name
- Click
Save
button
- Click
- On the left menu, click
Users
- Click
Create new user
button - Set
ivan.franchin
toUsername
field - Click
Create
- In
Credentials
tab- Click
Set password
button - Set the value
123
toPassword
andPassword confirmation
- Disable the
Temporary
field toggle switch - Click
Save
button - Confirm by clicking
Save password
button
- Click
- In
Role Mappings
tab- Click
Assign role
button - Click
Filter by realm roles
dropdown button and selectFilter by clients
- Select
[book-service] manage_books
name and clickAssign
button
- Click
-
Open a new terminal and navigate to
springboot-keycloak-mongodb-testcontainers
root folder -
Run the following command to start the application
./gradlew book-service:clean book-service:bootRun --args='--server.port=9080'
-
In a terminal, navigate to
springboot-keycloak-mongodb-testcontainers
root folder -
Build Docker Image
./docker-build.sh
Environment Variable Description MONGODB_HOST
Specify host of the Mongo
database to use (defaultlocalhost
)MONGODB_PORT
Specify port of the Mongo
database to use (default27017
)KEYCLOAK_HOST
Specify host of the Keycloak
to use (defaultlocalhost
)KEYCLOAK_PORT
Specify port of the Keycloak
to use (default8080
) -
Run
book-service
docker container, joining it to project Docker networkdocker run --rm --name book-service \ -p 9080:8080 \ -e MONGODB_HOST=mongodb \ -e KEYCLOAK_HOST=keycloak \ --network=springboot-keycloak-mongodb-testcontainers-net \ ivanfranchin/book-service:1.0.0
-
In a terminal, create an environment variable that contains the
Client Secret
generated byKeycloak
tobook-service
at Configure Keycloak stepBOOK_SERVICE_CLIENT_SECRET=...
-
When running book-service with Gradle
Run the commands below to get an access token for
ivan.franchin
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=ivan.franchin" \ -d "password=123" \ -d "grant_type=password" \ -d "client_secret=$BOOK_SERVICE_CLIENT_SECRET" \ -d "client_id=book-service" | jq -r .access_token) echo $ACCESS_TOKEN
-
When running book-service as a Docker Container
Run the commands below to get an access token for
ivan.franchin
ACCESS_TOKEN=$( docker run -t --rm -e CLIENT_SECRET=$BOOK_SERVICE_CLIENT_SECRET --network springboot-keycloak-mongodb-testcontainers-net 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=ivan.franchin" \ -d "password=123" \ -d "grant_type=password" \ -d "client_secret=$CLIENT_SECRET" \ -d "client_id=book-service"' | jq -r .access_token) echo $ACCESS_TOKEN
Note: We are running a alpine/curl Docker container and joining it to the project Docker network. By informing
"keycloak:8080"
host/port we won't have the error complaining about an invalid token due to an invalid token issuer. -
In jwt.io, you can decode and verify the
JWT
access token
-
In terminal, call the endpoint
GET /api/books
curl -i http://localhost:9080/api/books
It should return:
HTTP/1.1 200 []
-
Try to call the endpoint
POST /api/books
, without access tokencurl -i -X POST http://localhost:9080/api/books \ -H "Content-Type: application/json" \ -d '{"authorName": "Ivan Franchin", "title": "Java 8", "price": 10.5}'
It should return:
HTTP/1.1 401
-
Get the Access Token as explained on section Getting Access Token
-
Call the endpoint
POST /api/books
, now informing the access tokencurl -i -X POST http://localhost:9080/api/books \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"authorName": "Ivan Franchin", "title": "Java 8", "price": 10.5}'
It should return something like
HTTP/1.1 201 {"id":"612f4f9438e39e473c4d098b", "authorName":"Ivan Franchin", "title":"Java 8", "price":10.5}
-
Click
GET /api/books
to open it. Then, clickTry it out
button and, finally, clickExecute
button.It will return a http status code
200
and an empty list or a list with some books if you've already added them -
Now, let's try to call a secured endpoint without authentication. Click
POST /api/books
to open it. Then, clickTry it out
button (you can use the default values) and, finally, clickExecute
button.It will return
Code: 401 Details: Error: response status is 401
-
Get the Access Token as explained on section Getting Access Token
-
Copy the token generated and go back to
Swagger
-
Click the
Authorize
button and paste the access token in theValue
field. Then, clickAuthorize
and, to finalize, clickClose
-
Go to
POST /api/books
, clickTry it out
and, finally, clickExecute
button.It should return something like
HTTP/1.1 201 { "id": "612f502f38e39e473c4d098c", "authorName": "Ivan Franchin", "title": "SpringBoot", "price": 10.5 }
-
MongoDB
List books
docker exec -it mongodb mongosh bookdb db.books.find()
Type
exit
to get out of MongoDB shell
- To stop
book-service
, go to the terminal where the application is running and pressCtrl+C
; - To stop the Docker containers started using
./init-environment.sh
script, make sure you are inspringboot-keycloak-mongodb-testcontainers
and run the script below:./shutdown-environment.sh
To remove the Docker image created by this project, go to a terminal and, inside springboot-keycloak-mongodb-testcontainers
root folder, run the following script
./remove-docker-images.sh
-
In a terminal and inside
springboot-keycloak-mongodb-testcontainers
root folder, run the command below to run unit and integration tests./gradlew book-service:clean book-service:assemble \ book-service:cleanTest \ book-service:test \ book-service:integrationTest
Note: During integration tests,
Testcontainers
will start automaticallyMongoDB
andKeycloak
containers before the tests begin and shuts them down when the tests finish. -
From
springboot-keycloak-mongodb-testcontainers
root folder, Unit Testing Report can be found atbook-service/build/reports/tests/test/index.html
-
From
springboot-keycloak-mongodb-testcontainers
root folder, Integration Testing Report can be found atbook-service/build/reports/tests/integrationTest/index.html