{"id":18048034,"url":"https://github.com/ivangfr/springboot-kong-keycloak","last_synced_at":"2025-04-10T09:47:15.815Z","repository":{"id":111328641,"uuid":"415682137","full_name":"ivangfr/springboot-kong-keycloak","owner":"ivangfr","description":"Goal: create a Spring Boot app called book-service accessible only through the Kong API gateway. In Kong, the kong-oidc plugin will be installed, enabling communication between Kong and Keycloak. This setup ensures that when Kong receives a request for book-service, it validates the request in conjunction with Keycloak to ensure its authenticity.","archived":false,"fork":false,"pushed_at":"2025-01-14T11:54:43.000Z","size":1166,"stargazers_count":34,"open_issues_count":0,"forks_count":20,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-24T08:42:26.136Z","etag":null,"topics":["docker","graalvm","java","keycloak","kong","kong-oidc","mongodb","mysql","native","postgresql","spring-boot","spring-data-mongodb","spring-web-mvc"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivangfr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"ivangfr"}},"created_at":"2021-10-10T19:33:59.000Z","updated_at":"2025-02-17T22:05:36.000Z","dependencies_parsed_at":"2025-01-14T12:48:59.955Z","dependency_job_id":"662bac36-49d7-44de-ad0a-4cbf13d5de4d","html_url":"https://github.com/ivangfr/springboot-kong-keycloak","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fspringboot-kong-keycloak","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fspringboot-kong-keycloak/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fspringboot-kong-keycloak/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fspringboot-kong-keycloak/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivangfr","download_url":"https://codeload.github.com/ivangfr/springboot-kong-keycloak/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248196713,"owners_count":21063490,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","graalvm","java","keycloak","kong","kong-oidc","mongodb","mysql","native","postgresql","spring-boot","spring-data-mongodb","spring-web-mvc"],"created_at":"2024-10-30T20:11:03.969Z","updated_at":"2025-04-10T09:47:15.790Z","avatar_url":"https://github.com/ivangfr.png","language":"Shell","funding_links":["https://github.com/sponsors/ivangfr"],"categories":[],"sub_categories":[],"readme":"# springboot-kong-keycloak\n\nThe goal is to create a [`Spring Boot`](https://docs.spring.io/spring-boot/index.html) application to manage books, called `book-service` and secure it by using [`Kong`](https://konghq.com/products/kong-gateway) API gateway and [`Keycloak`](https://www.keycloak.org) OpenID Connect Provider.\n\n\u003e **Note**: In [`kubernetes-minikube-environment`](https://github.com/ivangfr/kubernetes-minikube-environment/tree/master/book-service-kong-keycloak) repository, it's shown how to deploy this project in `Kubernetes` (`Minikube`)\n\n## Proof-of-Concepts \u0026 Articles\n\nOn [ivangfr.github.io](https://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.\n\n## Additional Readings\n\n- \\[**Medium**\\] [**Using Kong to secure a Simple Spring Boot REST API with Kong OIDC plugin and Keycloak**](https://medium.com/@ivangfr/using-kong-to-secure-a-simple-spring-boot-rest-api-with-kong-oidc-plugin-and-keycloak-c8fa8de32e6e)\n- \\[**Medium**\\] [**Using Kong to secure a Simple Spring Boot REST API with Basic Authentication plugin**](https://medium.com/@ivangfr/using-kong-to-secure-a-simple-spring-boot-rest-api-with-basic-authentication-plugin-90f3529043f3)\n- \\[**Medium**\\] [**Using Kong to secure a Simple Spring Boot REST API with LDAP Authentication plugin**](https://medium.com/@ivangfr/using-kong-to-secure-a-simple-spring-boot-rest-api-with-ldap-authentication-plugin-3a499e01382a)\n- \\[**Medium**\\] [**Using Kong to configure Rate Limiting to a Simple Spring Boot REST API**](https://medium.com/@ivangfr/using-kong-to-configure-rate-limiting-to-a-simple-spring-boot-rest-api-33b1899077d)\n- \\[**Medium**\\] [**Implementing and Securing a Simple Spring Boot REST API with Keycloak**](https://medium.com/@ivangfr/how-to-secure-a-spring-boot-app-with-keycloak-5a931ee12c5a)\n\n## Project Diagram\n\n![project-diagram](documentation/project-diagram.jpeg)\n\nAs we can see from the diagram, `book-service` will only be reachable through `Kong` API gateway.\n\nIn `Kong`, it's installed [`kong-oidc`](https://github.com/nokia/kong-oidc) plugin that will enable the communication between `Kong` and `Keycloak` OpenID Connect Provider.\n\nThis way, when `Kong` receives a request to `book-service`, it will validate together with `Keycloak` whether it's a valid request.\n\nAlso, before redirecting to the request to the upstream service, a `Serverless Function (post-function)` will get the access token present in the `X-Userinfo` header provided by `kong-oidc` plugin, decode it, extract the `username` and `preferred_username`, and enrich the request with these two information before sending to `book-service`\n\n## Application\n\n- ### book-service\n\n  `Spring Boot` REST API application to manages books. The API doesn't have any security. `book-service` uses [`MongoDB`](https://www.mongodb.com) as storage.\n\n  Endpoints\n  ```\n     GET /actuator/health\n     GET /api/books\n    POST /api/books {\"isbn\": \"...\", \"title\": \"...\"}\n     GET /api/books/{isbn}\n  DELETE /api/books/{isbn}\n  ```\n\n## Prerequisites\n\n- [`Java 21`](https://www.oracle.com/java/technologies/downloads/#java21) or higher;\n- A containerization tool (e.g., [`Docker`](https://www.docker.com), [`Podman`](https://podman.io), etc.)\n- [`jq`](https://jqlang.github.io/jq/)\n\n## Run application during development using Maven\n\n- Open a terminal and navigate to `springboot-kong-keycloak` root folder\n\n- Run the command below to start `mongodb` Docker container\n  ```\n  docker run -d --name mongodb -p 27017:27017 mongo:8.0.3\n  ```\n\n- Run the command below to start `book-service`\n  ```\n  ./mvnw clean spring-boot:run --projects book-service\n  ```\n\n- Open another terminal and call application endpoints\n  ```\n  curl -i localhost:9080/api/books\n  curl -i -X POST localhost:9080/api/books -H \"Content-Type: application/json\" -d '{\"isbn\":\"123\", \"title\":\"Kong \u0026 Keycloak\"}'\n  curl -i localhost:9080/api/books/123\n  curl -i -X DELETE localhost:9080/api/books/123\n  curl -i localhost:9080/actuator/health\n  ```\n\n- To stop\n  - `book-service`, go to the terminal where it's running and press `Ctrl+C`\n  - `mongodb` Docker container, go to a terminal and run the following command\n    ```\n    docker rm -fv mongodb\n    ```\n\n## Build application Docker Image\n\n- In a terminal, make sure you are in `springboot-kong-keycloak` root folder\n\n- Build Docker Image\n  - JVM\n    ```\n    ./build-docker-images.sh\n    ```\n  - Native\n    ```\n    ./build-docker-images.sh native\n    ```\n\n  | Environment Variable | Description                                                       |\n  |----------------------|-------------------------------------------------------------------|\n  | `MONGODB_HOST`       | Specify host of the `Mongo` database to use (default `localhost`) |\n  | `MONGODB_PORT`       | Specify port of the `Mongo` database to use (default `27017`)     |\n\n## Test application Docker Image\n\n- In a terminal, create a Docker network\n  ```\n  docker network create springboot-kong-keycloak-net\n  ```\n\n- Run the command below to start `mongodb` Docker container\n  ```\n  docker run -d --name mongodb -p 27017:27017 --network springboot-kong-keycloak-net mongo:8.0.3\n  ```\n\n- Run the following command to start `book-service` Docker container\n  ```\n  docker run --rm -p 9080:9080 --name book-service -e MONGODB_HOST=mongodb --network springboot-kong-keycloak-net ivanfranchin/book-service:1.0.0\n  ```\n\n- Open another terminal and call application endpoints\n  ```\n  curl -i localhost:9080/api/books\n  curl -i -X POST localhost:9080/api/books -H \"Content-Type: application/json\" -d '{\"isbn\":\"123\", \"title\":\"Kong \u0026 Keycloak\"}'\n  curl -i localhost:9080/api/books/123\n  curl -i -X DELETE localhost:9080/api/books/123\n  curl -i localhost:9080/actuator/health\n  ```\n\n- To stop\n  - `book-service`, go to the terminal where it's running and press `Ctrl+C`\n  - `mongodb` Docker container, go to a terminal and run the following command\n    ```\n    docker rm -fv mongodb\n    ```\n  - remove Docker network\n    ```\n    docker network rm springboot-kong-keycloak-net\n    ```\n\n## Initialize Environment\n\n- In a terminal, make use you are in `springboot-kong-keycloak` root folder\n\n- Run the following script\n  ```\n  ./init-environment.sh\n  ```\n\n\u003e **Note**: `book-service` application is running as a Docker container. The container does not expose any port to HOST machine. So, it cannot be accessed directly, forcing the caller to use `Kong` as gateway server in order to access it.\n\n## Configure Keycloak\n\n- In a terminal, make sure you are in `springboot-kong-keycloak` root folder\n\n- Run the following script to configure `Keycloak` for `book-service` application\n  ```\n  ./init-keycloak.sh\n  ```\n\n  This script creates:\n  - `company-services` realm;\n  - `book-service` client;\n  - user with _username_ `ivan.franchin` and _password_ `123`.\n\n- 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\n\n- You can check the configuration in `Keycloak` by accessing http://localhost:8080. The credentials are `admin/admin`.\n\n## Configure Kong\n\n- In a terminal, make sure you are in `springboot-kong-keycloak` root folder\n\n- Create an environment variable that contains the `Client Secret` generated by `Keycloak` to `book-service` at [Configure Keycloak](#configure-keycloak) step\n  ```\n  BOOK_SERVICE_CLIENT_SECRET=...\n  ```\n\n- Run the following script to configure `Kong` for `book-service` application\n  ```\n  ./init-kong.sh $BOOK_SERVICE_CLIENT_SECRET\n  ```\n  \n  This script creates:\n  - service to `book-service`;\n  - route to `/actuator` path;\n  - route to `/api` path;\n  - add `kong-oidc` plugin to route of `/api` path. It will authenticate users against `Keycloak` OpenID Connect Provider;\n  - add `serverless function (post-function)` plugin to route of `/api` path. It gets the access token present in the `X-Userinfo` header provided by `kong-oidc` plugin, decoded it, extracts the `username` and `preferred_username`, and enriches the request with these two information before sending to `book-service`.\n\n## Testing\n\n- Try to call the public `GET /actuator/health` endpoint\n  ```\n  curl -i localhost:8000/actuator/health -H 'Host: book-service'\n  ```\n  It should return\n  ```\n  HTTP/1.1 200\n  {\"status\":\"UP\"}\n  ```\n\n- Try to call the private `GET /api/books` endpoint without access token\n  ```\n  curl -i localhost:8000/api/books -H 'Host: book-service'\n  ```\n  It should return\n  ```\n  HTTP/1.1 401 Unauthorized\n  no Authorization header found\n  ```\n\n- Get `ivan.franchin` access token\n  ```\n  ACCESS_TOKEN=$(./get-access-token.sh $BOOK_SERVICE_CLIENT_SECRET) \u0026\u0026 echo $ACCESS_TOKEN\n  ```\n  \u003e **Note**: In `jwt.io`, you can decode and verify the `JWT` access token\n\n- Call again the private `GET /api/books` endpoint using the access token\n  ```\n  curl -i localhost:8000/api/books \\\n    -H 'Host: book-service' \\\n    -H \"Authorization: Bearer $ACCESS_TOKEN\"\n  ```\n  It should return\n  ```\n  HTTP/1.1 200\n  []\n  ```\n\n- You can try other endpoints using access token\n\n  Create book\n  ```\n  curl -i -X POST localhost:8000/api/books \\\n    -H 'Host: book-service' \\\n    -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n    -H \"Content-Type: application/json\" -d '{\"isbn\": \"123\", \"title\": \"Kong \u0026 Keycloak\"}'\n  ```\n  \n  Get book\n  ```\n  curl -i localhost:8000/api/books/123 \\\n    -H 'Host: book-service' \\\n    -H \"Authorization: Bearer $ACCESS_TOKEN\"\n  ```\n  \n  Delete book \n  ```\n  curl -i -X DELETE localhost:8000/api/books/123 \\\n    -H 'Host: book-service' \\\n    -H \"Authorization: Bearer $ACCESS_TOKEN\"\n  ```\n\n## Useful Links \u0026 Commands\n\n- **MongoDB**\n\n  List books\n  ```\n  docker exec -it mongodb mongo bookdb\n  db.books.find()\n  ```\n  \u003e Type `exit` to get out of MongoDB shell\n\n## Shutdown\n\nIn a terminal and, inside `springboot-kong-keycloak` root folder, run the following script\n```\n./shutdown-environment.sh\n```\n\n## Cleanup\n\nTo remove the Docker image created by this project, in a terminal and, inside `springboot-kong-keycloak` root folder, run the script below\n```\n./remove-docker-images.sh\n```\n\n## References\n\n- https://www.jerney.io/secure-apis-kong-keycloak-1/\n- https://github.com/d4rkstar/kong-konga-keycloak\n\n## Issues\n\n- Unable to upgrade to `kong` version `3.x` because `BasePlugin` class was deprecated in `kong` version `2.4.x` and removed in version `3.0.x` [link](https://support.konghq.com/support/s/article/Custom-plugins-not-loading-with-no-LuaRocks-module-found-for-kong-plugins-base-plugin). Now, `kong-oidc` needs to support `kong` version `3.x` [issue](https://github.com/nokia/kong-oidc/issues/213);\n\n- When upgrading `postgres` to a version above `13.x` (using current kong version), there is an error while running `kong`\n  ```\n  [error] 1#0: init_by_lua error: /usr/local/share/lua/5.1/pgmoon/init.lua:273: module 'openssl.rand' not found:No LuaRocks module found for openssl.rand\n  \tno field package.preload['openssl.rand']\n  \tno file './openssl/rand.lua'\n  \tno file './openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand.ljbc'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand/init.ljbc'\n  \tno file '/usr/local/openresty/lualib/openssl/rand.ljbc'\n  \tno file '/usr/local/openresty/lualib/openssl/rand/init.ljbc'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand.lua'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/lualib/openssl/rand.lua'\n  \tno file '/usr/local/openresty/lualib/openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/luajit/share/luajit-2.1.0-beta3/openssl/rand.lua'\n  \tno file '/usr/local/share/lua/5.1/openssl/rand.lua'\n  \tno file '/usr/local/share/lua/5.1/openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/luajit/share/lua/5.1/openssl/rand.lua'\n  \tno file '/usr/local/openresty/luajit/share/lua/5.1/openssl/rand/init.lua'\n  \tno file '/home/kong/.luarocks/share/lua/5.1/openssl/rand.lua'\n  \tno file '/home/kong/.luarocks/share/lua/5.1/openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand.so'\n  \tno file '/usr/local/openresty/lualib/openssl/rand.so'\n  \tno file './openssl/rand.so'\n  \tno file '/usr/local/lib/lua/5.1/openssl/rand.so'\n  \tno file '/usr/local/openresty/luajit/lib/lua/5.1/openssl/rand.so'\n  \tno file '/usr/local/lib/lua/5.1/loadall.so'\n  \tno file '/home/kong/.luarocks/lib/lua/5.1/openssl/rand.so'\n  \tno file '/usr/local/openresty/site/lualib/openssl.so'\n  \tno file '/usr/local/openresty/lualib/openssl.so'\n  \tno file './openssl.so'\n  \tno file '/usr/local/lib/lua/5.1/openssl.so'\n  \tno file '/usr/local/openresty/luajit/lib/lua/5.1/openssl.so'\n  \tno file '/usr/local/lib/lua/5.1/loadall.so'\n  \tno file '/home/kong/.luarocks/lib/lua/5.1/openssl.so'\n  stack traceback:\n  \t[C]: in function 'require'\n  \t/usr/local/share/lua/5.1/pgmoon/init.lua:273: in function 'auth'\n  \t/usr/local/share/lua/5.1/pgmoon/init.lua:213: in function 'connect'\n  \t.../share/lua/5.1/kong/db/strategies/postgres/connector.lua:216: in function 'connect'\n  \t.../share/lua/5.1/kong/db/strategies/postgres/connector.lua:516: in function 'query'\n  \t.../share/lua/5.1/kong/db/strategies/postgres/connector.lua:284: in function 'init'\n  \t/usr/local/share/lua/5.1/kong/db/init.lua:141: in function 'init_connector'\n  \t/usr/local/share/lua/5.1/kong/init.lua:503: in function 'init'\n  \tinit_by_lua:3: in main chunk\n  nginx: [error] init_by_lua error: /usr/local/share/lua/5.1/pgmoon/init.lua:273: module 'openssl.rand' not found:No LuaRocks module found for openssl.rand\n  \tno field package.preload['openssl.rand']\n  \tno file './openssl/rand.lua'\n  \tno file './openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand.ljbc'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand/init.ljbc'\n  \tno file '/usr/local/openresty/lualib/openssl/rand.ljbc'\n  \tno file '/usr/local/openresty/lualib/openssl/rand/init.ljbc'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand.lua'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/lualib/openssl/rand.lua'\n  \tno file '/usr/local/openresty/lualib/openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/luajit/share/luajit-2.1.0-beta3/openssl/rand.lua'\n  \tno file '/usr/local/share/lua/5.1/openssl/rand.lua'\n  \tno file '/usr/local/share/lua/5.1/openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/luajit/share/lua/5.1/openssl/rand.lua'\n  \tno file '/usr/local/openresty/luajit/share/lua/5.1/openssl/rand/init.lua'\n  \tno file '/home/kong/.luarocks/share/lua/5.1/openssl/rand.lua'\n  \tno file '/home/kong/.luarocks/share/lua/5.1/openssl/rand/init.lua'\n  \tno file '/usr/local/openresty/site/lualib/openssl/rand.so'\n  \tno file '/usr/local/openresty/lualib/openssl/rand.so'\n  \tno file './openssl/rand.so'\n  \tno file '/usr/local/lib/lua/5.1/openssl/rand.so'\n  \tno file '/usr/local/openresty/luajit/lib/lua/5.1/openssl/rand.so'\n  \tno file '/usr/local/lib/lua/5.1/loadall.so'\n  \tno file '/home/kong/.luarocks/lib/lua/5.1/openssl/rand.so'\n  \tno file '/usr/local/openresty/site/lualib/openssl.so'\n  \tno file '/usr/local/openresty/lualib/openssl.so'\n  \tno file './openssl.so'\n  \tno file '/usr/local/lib/lua/5.1/openssl.so'\n  \tno file '/usr/local/openresty/luajit/lib/lua/5.1/openssl.so'\n  \tno file '/usr/local/lib/lua/5.1/loadall.so'\n  \tno file '/home/kong/.luarocks/lib/lua/5.1/openssl.so'\n  stack traceback:\n  \t[C]: in function 'require'\n  \t/usr/local/share/lua/5.1/pgmoon/init.lua:273: in function 'auth'\n  \t/usr/local/share/lua/5.1/pgmoon/init.lua:213: in function 'connect'\n  \t.../share/lua/5.1/kong/db/strategies/postgres/connector.lua:216: in function 'connect'\n  \t.../share/lua/5.1/kong/db/strategies/postgres/connector.lua:516: in function 'query'\n  \t.../share/lua/5.1/kong/db/strategies/postgres/connector.lua:284: in function 'init'\n  \t/usr/local/share/lua/5.1/kong/db/init.lua:141: in function 'init_connector'\n  \t/usr/local/share/lua/5.1/kong/init.lua:503: in function 'init'\n  \tinit_by_lua:3: in main chunk\n  ```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivangfr%2Fspringboot-kong-keycloak","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivangfr%2Fspringboot-kong-keycloak","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivangfr%2Fspringboot-kong-keycloak/lists"}