springboot-keycloak-mongodb-testcontainers
springboot-keycloak-mongodb-testcontainers copied to clipboard
The goals of this project are: 1) Create a Spring Boot application that manages books, called book-service; 2) Use Keycloak as OpenID Connect Provider; 3) Test using Testcontainers; 4) Explore the uti...
springboot-keycloak-mongodb-testcontainers
The goals of this project are:
- Create a
Spring Bootapplication that manages books, calledbook-service; - Use
Keycloakas OpenID Connect Provider; - Test using
Testcontainers; - Explore the utilities and annotations that
Spring Bootprovides when testing applications.
Proof-of-Concepts & Articles
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.
Additional Readings
- [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
Application
-
book-service
Spring BootWeb application that manages books.MongoDBis 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, run the script below
./init-environment.sh
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-testcontainersroot folder -
Run the following script to configure
Keycloakforbook-serviceapplication./init-keycloak.shThis script creates:
company-servicesrealm;book-serviceclient;manage_booksclient role;- user with username
ivan.franchinand password123and with the rolemanage_booksassigned.
-
The
book-serviceclient 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
Keycloakby accessing http://localhost:8080. The credentials areadmin/admin.
Using Keycloak Website
Login
-
Access http://localhost:8080
-
Login with the credentials
Username: admin Password: admin
Create a new Realm
- On the left menu, click the dropdown button that contains
Keycloakand then, clickCreate Realmbutton - Set
company-servicesto theRealm namefield and clickCreatebutton
Disable Required Action Verify Profile
- On the left menu, click
Authentication - Select
Required actionstab - Disable
Verify Profile
Create a new Client
- On the left menu, click
Clients - Click
Create clientbutton - In
General Settings- Set
book-servicetoClient ID - Click
Nextbutton
- Set
- In
Capability config- Enable
Client authenticationtoggle switch - Click
Nextbutton
- Enable
- In
Login settingstab- Set
http://localhost:9080/*toValid redirect URIs - Click
Savebutton
- Set
- In
Credentialstab, you can find the secret generated forbook-service - In
Rolestab- Click
Create rolebutton - Set
manage_bookstoRole Name - Click
Savebutton
- Click
Create a new User
- On the left menu, click
Users - Click
Create new userbutton - Set
ivan.franchintoUsernamefield - Click
Create - In
Credentialstab- Click
Set passwordbutton - Set the value
123toPasswordandPassword confirmation - Disable the
Temporaryfield toggle switch - Click
Savebutton - Confirm by clicking
Save passwordbutton
- Click
- In
Role Mappingstab- Click
Assign rolebutton - Click
Filter by realm rolesdropdown button and selectFilter by clients - Select
[book-service] manage_booksname and clickAssignbutton
- Click
Running book-service with Gradle
-
Open a new terminal and navigate to
springboot-keycloak-mongodb-testcontainersroot folder -
Run the following command to start the application
./gradlew book-service:clean book-service:bootRun --args='--server.port=9080'
Running book-service as a Docker Container
-
In a terminal, navigate to
springboot-keycloak-mongodb-testcontainersroot folder -
Build Docker Image
./docker-build.shEnvironment Variable Description MONGODB_HOSTSpecify host of the Mongodatabase to use (defaultlocalhost)MONGODB_PORTSpecify port of the Mongodatabase to use (default27017)KEYCLOAK_HOSTSpecify host of the Keycloakto use (defaultlocalhost)KEYCLOAK_PORTSpecify port of the Keycloakto use (default8080) -
Run
book-servicedocker 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
Getting Access Token
-
In a terminal, create an environment variable that contains the
Client Secretgenerated byKeycloaktobook-serviceat Configure Keycloak stepBOOK_SERVICE_CLIENT_SECRET=... -
When running book-service with Gradle
Run the commands below to get an access token for
ivan.franchinACCESS_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.franchinACCESS_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_TOKENNote: 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
JWTaccess token
Test using cURL
-
In terminal, call the endpoint
GET /api/bookscurl -i http://localhost:9080/api/booksIt 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}
Test using Swagger
-
Access http://localhost:9080/swagger-ui.html
-
Click
GET /api/booksto open it. Then, clickTry it outbutton and, finally, clickExecutebutton.It will return a http status code
200and 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/booksto open it. Then, clickTry it outbutton (you can use the default values) and, finally, clickExecutebutton.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
Authorizebutton and paste the access token in theValuefield. Then, clickAuthorizeand, to finalize, clickClose -
Go to
POST /api/books, clickTry it outand, finally, clickExecutebutton.It should return something like
HTTP/1.1 201 { "id": "612f502f38e39e473c4d098c", "authorName": "Ivan Franchin", "title": "SpringBoot", "price": 10.5 }
Useful Links & Commands
-
MongoDB
List books
docker exec -it mongodb mongosh bookdb db.books.find()Type
exitto get out of MongoDB shell
Shutdown
- 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.shscript, make sure you are inspringboot-keycloak-mongodb-testcontainersand run the script below:./shutdown-environment.sh
Cleanup
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
Running Unit and Integration Tests
-
In a terminal and inside
springboot-keycloak-mongodb-testcontainersroot 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:integrationTestNote: During integration tests,
Testcontainerswill start automaticallyMongoDBandKeycloakcontainers before the tests begin and shuts them down when the tests finish. -
From
springboot-keycloak-mongodb-testcontainersroot folder, Unit Testing Report can be found atbook-service/build/reports/tests/test/index.html -
From
springboot-keycloak-mongodb-testcontainersroot folder, Integration Testing Report can be found atbook-service/build/reports/tests/integrationTest/index.html