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 for testing applications.
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] 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 the springboot-keycloak-mongodb-testcontainers
root folder, run the script below
./init-environment.sh
There are two ways: running a script or using the Keycloak
website
In a terminal, make sure you are inside the
springboot-keycloak-mongodb-testcontainers
root folderRun the following script to configure
Keycloak
forbook-service
application./init-keycloak.sh
This script will create:
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
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 the
springboot-keycloak-mongodb-testcontainers
root folderRun the following command to start the application
./gradlew book-service:clean book-service:bootRun --args='--server.port=9080'
In a terminal, navigate to the
springboot-keycloak-mongodb-testcontainers
root folderBuild Docker Image
./build-docker-images.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 specifying
"keycloak:8080"
as host/port, we won't encounter the error related 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 the
./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 the springboot-keycloak-mongodb-testcontainers
root folder, run the following script:
./remove-docker-images.sh
In a terminal and inside the
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 automatically startMongoDB
andKeycloak
containers before the tests begin and shut them down when the tests finish.From the
springboot-keycloak-mongodb-testcontainers
root folder, the Unit Testing Report can be found at:book-service/build/reports/tests/test/index.html
From the
springboot-keycloak-mongodb-testcontainers
root folder, the Integration Testing Report can be found at:book-service/build/reports/tests/integrationTest/index.html