springboot-keycloak-mongodb-testcontainers
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.
Application
-
book-service
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.
Prerequisites
Start Environment
-
Open a terminal and inside
springboot-keycloak-mongodb-testcontainers
root folder rundocker-compose up -d
-
Wait for
keycloak
andmongodb
Docker containers to be up and running. To check it, rundocker-compose ps
Configure Keycloak
There are two ways: running a script or using Keycloak
website
Running Script
-
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
.
Using Keycloak Website
Login
-
Login with the credentials
Username: admin Password: admin
Create a new Realm
- Go to top-left corner and hover the mouse over
Master
realm. Click theAdd realm
blue button that will appear - Set
company-services
to theName
field and clickCreate
button
Create a new Client
- On the left menu, click
Clients
- Click
Create
button - Set
book-service
toClient ID
and clickSave
button - In
Settings
tab- Set
confidential
toAccess Type
- Set
http://localhost:9080/*
toValid Redirect URIs
- Click
Save
button
- Set
- In
Credentials
tab, you can find the secretKeycloak
generated forsimple-service
- In
Roles
tab- Click
Add Role
button - Set
manage_books
toRole Name
and clickSave
button
- Click
Create a new User
- On the left menu, click
Users
- Click
Add User
button - Set
ivan.franchin
toUsername
field - Click
Save
- In
Credentials
tab- Set the value
123
toPassword
andPassword Confirmation
- Turn off the
Temporary
field - Click
Set password
button - Confirm the popup and click
Set Password
red button
- Set the value
- In
Role Mappings
tab- Select
book-service
on the combo-boxClient Roles
- In
Available Roles
, selectmanage_books
role and clickAdd selected >>
button
- Select
Running book-service with Gradle
-
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'
-
The application Swagger URL is http://localhost:9080/swagger-ui.html
Getting Access Token
-
Open a terminal and make sure you are in
springboot-keycloak-mongodb-testcontainers
root folder -
Create an environment variable that contains the
Client Secret
generated byKeycloak
tobook-service
at Configure Keycloak stepBOOK_SERVICE_CLIENT_SECRET=...
-
Run the commands below to get an access token for
ivan.franchin
ACCESS_TOKEN=$(./get-access-token.sh $BOOK_SERVICE_CLIENT_SECRET) && echo $ACCESS_TOKEN
-
The access token has a default expiration time of
5 minutes
Test using cURL
-
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 302
-
If you do not have the access token stored in
ACCESS_TOKEN
environment variable, get it by following the steps describe at 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 }
Test using Swagger
-
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
Failed to fetch
-
Get the access token as explained at 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 }
Running book-service as a Docker Container
-
In a terminal, navigate to
springboot-keycloak-mongodb-testcontainers
root folder -
Build Docker Image
./gradlew book-service:clean book-service:jibDockerBuild -x test -x integrationTest
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 docker-compose networkdocker run --rm --name book-service \ -p 9080:8080 \ -e MONGODB_HOST=mongodb \ -e KEYCLOAK_HOST=keycloak \ --network=springboot-keycloak-mongodb-testcontainers_default \ ivanfranchin/book-service:1.0.0
-
Open a new terminal and create an environment variable that contains the
Client Secret
generated byKeycloak
BOOK_SERVICE_CLIENT_SECRET=...
-
In order to get the access token from
Keycloak
, run the following commandsNote: the
"keycloak:8080"
string is informed in the second argument of the script. It changes"localhost:8080"
host/port inside the script. This way, we won't have the error complaining about an invalid token due to an invalid token issuer.ACCESS_TOKEN=$(./get-access-token.sh $BOOK_SERVICE_CLIENT_SECRET "keycloak:8080") && echo $ACCESS_TOKEN
-
Test using cURL or Swagger are the same as explained above
Useful Links & Commands
-
MongoDB
List books
docker exec -it mongodb mongo bookdb db.books.find()
Type
exit
to get out of MongoDB shell -
jwt.io
With jwt.io you can inform the JWT token received from
Keycloak
and the online tool decodes the token, showing its header and payload.
Shutdown
- To stop
book-service
, go to the terminal where the application is running and pressCtrl+C
- To stop and remove docker-compose containers, networks and volumes, make sure you are in
springboot-keycloak-mongodb-testcontainers
and rundocker-compose down -v
Cleanup
To remove the Docker image created by this project, go to a terminal and run the following command
docker rmi ivanfranchin/book-service:1.0.0
Running Unit and Integration Tests
-
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
Issues
- Disabled
BookControllerTest
because I am getting the exception below. TheadapterConfig
parameter passed tointernalBuild
isnull
java.lang.NullPointerException at org.keycloak.adapters.KeycloakDeploymentBuilder.internalBuild(KeycloakDeploymentBuilder.java:57) at org.keycloak.adapters.KeycloakDeploymentBuilder.build(KeycloakDeploymentBuilder.java:202) at org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver.resolve(KeycloakSpringBootConfigResolver.java:41) at org.keycloak.adapters.springsecurity.config.KeycloakSpringConfigResolverWrapper.resolve(KeycloakSpringConfigResolverWrapper.java:40) at org.keycloak.adapters.AdapterDeploymentContext.resolveDeployment(AdapterDeploymentContext.java:89) ...