{"id":18048033,"url":"https://github.com/ivangfr/springboot-keycloak-openldap","last_synced_at":"2025-04-04T11:15:23.504Z","repository":{"id":40617420,"uuid":"123774471","full_name":"ivangfr/springboot-keycloak-openldap","owner":"ivangfr","description":"The goal of this project is to create a simple Spring Boot REST API, called simple-service, and secure it with Keycloak. Furthermore, the API users will be loaded into Keycloak from OpenLDAP server.","archived":false,"fork":false,"pushed_at":"2025-03-02T19:00:17.000Z","size":3674,"stargazers_count":158,"open_issues_count":0,"forks_count":69,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-28T10:09:28.343Z","etag":null,"topics":["docker","graalvm","java","keycloak","ldap","native","oauth2-resource-server","openldap","phpldapadmin","spring-boot","spring-security","spring-web-mvc","springdoc-openapi"],"latest_commit_sha":null,"homepage":"","language":"Java","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":"2018-03-04T09:32:20.000Z","updated_at":"2025-03-03T17:41:42.000Z","dependencies_parsed_at":"2022-09-12T23:42:00.640Z","dependency_job_id":"f16252e2-854a-4dd0-bb53-061e22092c2f","html_url":"https://github.com/ivangfr/springboot-keycloak-openldap","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-keycloak-openldap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fspringboot-keycloak-openldap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fspringboot-keycloak-openldap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivangfr%2Fspringboot-keycloak-openldap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivangfr","download_url":"https://codeload.github.com/ivangfr/springboot-keycloak-openldap/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247166168,"owners_count":20894654,"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","ldap","native","oauth2-resource-server","openldap","phpldapadmin","spring-boot","spring-security","spring-web-mvc","springdoc-openapi"],"created_at":"2024-10-30T20:11:03.956Z","updated_at":"2025-04-04T11:15:23.486Z","avatar_url":"https://github.com/ivangfr.png","language":"Java","funding_links":["https://github.com/sponsors/ivangfr"],"categories":[],"sub_categories":[],"readme":"# springboot-keycloak-openldap\n\nThe goal of this project is to create a simple [Spring Boot](https://docs.spring.io/spring-boot/index.html) REST API, called `simple-service`, and secure it with [`Keycloak`](https://www.keycloak.org). Furthermore, the API users will be loaded into `Keycloak` from [`OpenLDAP`](https://www.openldap.org) server.\n\n\u003e **Note**: In the [`springboot-react-keycloak`](https://github.com/ivangfr/springboot-react-keycloak) repository, we have implemented a `movies-app` using `Keycloak` (with `PKCE`). This application consists of two services: the backend that was implemented using `Spring Boot` and the frontend implemented with `ReactJS`.\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**\\] [**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- \\[**Medium**\\] [**Implementing and Securing a Simple Spring Boot UI (Thymeleaf + RBAC) with Keycloak**](https://medium.com/@ivangfr/how-to-secure-a-simple-spring-boot-ui-thymeleaf-rbac-with-keycloak-ba9f30b9cb2b)\n- \\[**Medium**\\] [**Implementing and Securing a Spring Boot GraphQL API with Keycloak**](https://medium.com/@ivangfr/implementing-and-securing-a-spring-boot-graphql-api-with-keycloak-c461c86e3972)\n- \\[**Medium**\\] [**Setting Up OpenLDAP With Keycloak For User Federation**](https://medium.com/@ivangfr/setting-up-openldap-with-keycloak-for-user-federation-82c643b3a0e6)\n- \\[**Medium**\\] [**Integrating GitHub as a Social Identity Provider in Keycloak**](https://medium.com/@ivangfr/integrating-github-as-a-social-identity-provider-in-keycloak-982f521a622f)\n- \\[**Medium**\\] [**Integrating Google as a Social Identity Provider in Keycloak**](https://medium.com/@ivangfr/integrating-google-as-a-social-identity-provider-in-keycloak-c905577ec499)\n- \\[**Medium**\\] [**Building a Single Spring Boot App with Keycloak or Okta as IdP: Introduction**](https://medium.com/@ivangfr/building-a-single-spring-boot-app-with-keycloak-or-okta-as-idp-introduction-2814a4829aed)\n- \\[**Medium**\\] [**Implementing a Full Stack Web App using Spring-Boot and React**](https://medium.com/@ivangfr/implementing-a-full-stack-web-app-using-spring-boot-and-react-7db598df4452)\n- \\[**Medium**\\] [**Using Keycloak to secure a Full Stack Web App implemented with Spring-Boot and React**](https://medium.com/@ivangfr/using-keycloak-to-secure-a-full-stack-web-app-implemented-with-spring-boot-and-react-6b2d80fc5c12)\n\n## Project Diagram\n\n![project-diagram](documentation/project-diagram.jpeg)\n\n## Application\n\n- ### simple-service\n\n  `Spring Boot` Web Java application that exposes the following endpoints:\n  - `GET /api/public`: it's a not secured endpoint, everybody can access it;\n  - `GET /api/private`: it's a secured endpoint, only accessible by users that provide a `JWT` access token issued by `Keycloak` and the token must contain the role `USER`.\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## Start Environment\n\n- Open a terminal and inside the `springboot-keycloak-openldap` root folder run:\n  ```\n  docker compose up -d\n  ```\n\n- Just wait for the Docker containers to start running. The Keycloak Docker container usually takes longer. You can check its progress by running this command:\n  ```\n  docker logs -f keycloak\n  ```\n  \u003e Press `Ctrl+C` to exit\n\n  Once you see the following log, Keycloak has started:\n  ```\n  INFO  [io.quarkus] (main) Keycloak 26.1.3 on JVM (powered by Quarkus 3.15.3.1) started in 44.567s. Listening on: http://0.0.0.0:8080. Management interface listening on http://0.0.0.0:9000.\n  ```\n\n## Import OpenLDAP Users\n\nThe `LDIF` file that we will use, `springboot-keycloak-openldap/ldap/ldap-mycompany-com.ldif`, contains a predefined structure for `mycompany.com`. Basically, it has 2 groups (`developers` and `admin`) and 4 users (`Bill Gates`, `Steve Jobs`, `Mark Cuban` and `Ivan Franchin`). Additionally, it is defined that `Bill Gates`, `Steve Jobs` and `Mark Cuban` belong to `developers` group and `Ivan Franchin` belongs to `admin` group.\n```\nBill Gates \u003e username: bgates, password: 123\nSteve Jobs \u003e username: sjobs, password: 123\nMark Cuban \u003e username: mcuban, password: 123\nIvan Franchin \u003e username: ifranchin, password: 123\n```\n\nThere are two ways to import those users: by running a script or by using [`phpLDAPadmin`](https://github.com/leenooks/phpLDAPadmin).\n\n### Running a script\n\n- In a terminal and inside the `springboot-keycloak-openldap` root folder run:\n  ```\n  ./import-openldap-users.sh\n  ```\n\n- The command below can be used to check the imported users:\n  ```\n  ldapsearch -x -D \"cn=admin,dc=mycompany,dc=com\" \\\n    -w admin -H ldap://localhost:389 \\\n    -b \"ou=users,dc=mycompany,dc=com\" \\\n    -s sub \"(uid=*)\"\n  ```\n\n### Using phpLDAPadmin website\n\n- Access https://localhost:6443\n\n- Login with the credentials:\n  ```\n  Login DN: cn=admin,dc=mycompany,dc=com\n  Password: admin\n  ```\n\n- Import the file `springboot-keycloak-openldap/ldap/ldap-mycompany-com.ldif`.\n\n- You should see a tree like the one shown in the picture below:\n\n  ![phpldapadmin](documentation/phpldapadmin.jpeg)\n\n## Configure Keycloak\n\nThere are two ways: running a script or using `Keycloak` website.\n\n### Running a script\n\n- In a terminal, make sure you are inside the `springboot-keycloak-openldap` root folder.\n\n- Run the script below to configure `Keycloak` for `simple-service` application:\n  ```\n  ./init-keycloak.sh\n  ```\n\n  It creates `company-services` realm, `simple-service` client, `USER` client role, `ldap` federation and the users `bgates` and `sjobs` with the role `USER` assigned.\n\n- Copy `SIMPLE_SERVICE_CLIENT_SECRET` value that is shown at the end of the script. It will be needed whenever we call `Keycloak` to get a `JWT` access token to access `simple-service`.\n\n### Using Keycloak website\n\nPlease have a look at this **Medium** article, [**Setting Up OpenLDAP With Keycloak For User Federation**](https://medium.com/@ivangfr/setting-up-openldap-with-keycloak-for-user-federation-82c643b3a0e6)\n\n## Run simple-service using Maven\n\n- Open a new terminal and make sure you are in the `springboot-keycloak-openldap` root folder.\n\n- Start the application by running the following command:\n  ```\n  ./mvnw clean spring-boot:run --projects simple-service -Dspring-boot.run.jvmArguments=\"-Dserver.port=9080\"\n  ```\n\n## Test using curl\n\n1. Open a new terminal.\n\n2. Call the endpoint `GET /api/public`:\n   ```\n   curl -i http://localhost:9080/api/public\n   ```\n   \n   It should return:\n   ```\n   HTTP/1.1 200\n   It is public.\n   ```\n   \n3. Try to call the endpoint `GET /api/private` without authentication:\n   ```\n   curl -i http://localhost:9080/api/private\n   ```\n   \n   It should return:\n   ```\n   HTTP/1.1 401\n   ```\n\n4. Create an environment variable that contains the `Client Secret` generated by `Keycloak` to `simple-service` at [Configure Keycloak](#configure-keycloak) step:\n   ```\n   SIMPLE_SERVICE_CLIENT_SECRET=...\n   ```\n\n5. Run the command below to get an access token for `bgates` user:\n   ```\n   BGATES_ACCESS_TOKEN=$(curl -s -X POST \\\n     \"http://localhost:8080/realms/company-services/protocol/openid-connect/token\" \\\n     -H \"Content-Type: application/x-www-form-urlencoded\" \\\n     -d \"username=bgates\" \\\n     -d \"password=123\" \\\n     -d \"grant_type=password\" \\\n     -d \"client_secret=$SIMPLE_SERVICE_CLIENT_SECRET\" \\\n     -d \"client_id=simple-service\" | jq -r .access_token)\n   \n   echo $BGATES_ACCESS_TOKEN\n   ```\n   \u003e **Note**: In [jwt.io](https://jwt.io), you can decode and verify the `JWT` access token\n\n6. Call the endpoint `GET /api/private`:\n   ```\n   curl -i http://localhost:9080/api/private -H \"Authorization: Bearer $BGATES_ACCESS_TOKEN\"\n   ```\n   \n   It should return:\n   ```\n   HTTP/1.1 200\n   bgates, it is private.\n   ```\n\n7. The access token default expiration period is `5 minutes`. So, wait for this time and, using the same access token, try to call the private endpoint.\n\n   It should return:\n   ```\n   HTTP/1.1 401\n   WWW-Authenticate: Bearer realm=\"company-services\", error=\"invalid_token\", error_description=\"Token is not active\"\n   ```\n\n## Test using Swagger\n\n1. Access http://localhost:9080/swagger-ui.html\n\n   ![simple-service-swagger](documentation/simple-service-swagger.jpeg)\n\n2. Click `GET /api/public` to open it. Then, click `Try it out` button and, finally, click `Execute` button.\n\n   It should return:\n   ```\n   Code: 200\n   Response Body: It is public.\n   ```\n\n3. Now click `GET /api/private` secured endpoint. Let's try it without authentication. Then, click `Try it out` button and, finally, click `Execute` button.\n  \n   It should return:\n   ```\n   Code: 401\n   Details: Error: response status is 401\n   ```\n\n4. In order to access the private endpoint, you need an access token. So, open a terminal.\n\n5. Create an environment variable that contains the `Client Secret` generated by `Keycloak` to `simple-service` at [Configure Keycloak](#configure-keycloak) step:\n   ```\n   SIMPLE_SERVICE_CLIENT_SECRET=...\n   ```\n  \n6. Run the following commands:\n   ```\n   BGATES_ACCESS_TOKEN=$(curl -s -X POST \\\n     \"http://localhost:8080/realms/company-services/protocol/openid-connect/token\" \\\n     -H \"Content-Type: application/x-www-form-urlencoded\" \\\n     -d \"username=bgates\" \\\n     -d \"password=123\" \\\n     -d \"grant_type=password\" \\\n     -d \"client_secret=$SIMPLE_SERVICE_CLIENT_SECRET\" \\\n     -d \"client_id=simple-service\" | jq -r .access_token)\n     \n   echo $BGATES_ACCESS_TOKEN\n   ```\n\n7. Copy the token generated and go back to `Swagger`.\n\n8. Click `Authorize` button and paste the access token in the `Value` field. Then, click `Authorize` button and, to finalize, click `Close`.\n\n9. Go to `GET /api/private` and call this endpoint again, now with authentication.\n\n   It should return:\n   ```\n   Code: 200\n   Response Body: bgates, it is private.\n   ```\n\n## Using client_id and client_secret to get access token\n\nYou can get an access token for `simple-service` using `client_id` and `client_secret`\n\n### Configuration\n\n- Access http://localhost:8080;\n- Click the dropdown button that contains `Keycloak` and select `company-services`;\n- On the left menu, click `Clients`;\n- Select `simple-service` client;\n- In `Settings` tab:\n  - Go to `Capability config` and check `Service accounts roles` checkbox;\n  - Click `Save` button;\n- In `Service account roles` tab:\n  - Click `service-account-simple-service` link present in the info message;\n    \u003e \"To manage detail and group mappings, click on the username service-account-simple-service\"\n  - In `Role mapping` tab:\n    - Click `Assign role` button;\n    - Make sure the `Filter by clients` is selected in the first dropdown button;\n    - In `Search by role name`, type `simple-service` and press `Enter`;\n    - Select `[simple-service] USER` name and click `Assign` button;\n    - Now, `service-account-simple-service` has the role `USER` of the `simple-service` assigned.\n\n### Test\n\n1. Open a terminal.\n\n2. Create an environment variable that contains the `Client Secret` generated by `Keycloak` to `simple-service` at [Configure Keycloak](#configure-keycloak) step.\n   ```\n   SIMPLE_SERVICE_CLIENT_SECRET=...\n   ```\n  \n3. Run the following command:\n   ```\n   CLIENT_ACCESS_TOKEN=$(curl -s -X POST \\\n     \"http://localhost:8080/realms/company-services/protocol/openid-connect/token\" \\\n     -H \"Content-Type: application/x-www-form-urlencoded\" \\\n     -d \"grant_type=client_credentials\" \\\n     -d \"client_secret=$SIMPLE_SERVICE_CLIENT_SECRET\" \\\n     -d \"client_id=simple-service\" | jq -r .access_token)\n   \n   echo $CLIENT_ACCESS_TOKEN\n   ```\n  \n4. Try to call the endpoint `GET /api/private`:\n   ```\n   curl -i http://localhost:9080/api/private -H \"Authorization: Bearer $CLIENT_ACCESS_TOKEN\"\n   ```\n  \n   It should return:\n   ```\n   HTTP/1.1 200\n   service-account-simple-service, it is private.\n   ```\n\n## Running simple-service as a Docker container\n\n- In a terminal, make sure you are in the `springboot-keycloak-openldap` 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  | `KEYCLOAK_HOST`      | Specify host of the `Keycloak` to use (default `localhost`) |\n  | `KEYCLOAK_PORT`      | Specify port of the `Keycloak` to use (default `8080`)      |\n\n- Run Docker Container:\n  ```\n  docker run --rm --name simple-service \\\n    -p 9080:8080 \\\n    -e KEYCLOAK_HOST=keycloak \\\n    --network=springboot-keycloak-openldap_default \\\n    ivanfranchin/simple-service:1.0.0\n  ```\n\n- Open a new terminal.\n\n- Create an environment variable that contains the `Client Secret` generated by `Keycloak` to `simple-service` at [Configure Keycloak](#configure-keycloak) step.\n  ```\n  SIMPLE_SERVICE_CLIENT_SECRET=...\n  ```\n\n- Run the commands below to get an access token for `bgates` user:\n  ```\n  BGATES_TOKEN=$(\n    docker run -t --rm -e CLIENT_SECRET=$SIMPLE_SERVICE_CLIENT_SECRET --network springboot-keycloak-openldap_default alpine/curl:latest sh -c '\n      curl -s -X POST http://keycloak:8080/realms/company-services/protocol/openid-connect/token \\\n        -H \"Content-Type: application/x-www-form-urlencoded\" \\\n        -d \"username=bgates\" \\\n        -d \"password=123\" \\\n        -d \"grant_type=password\" \\\n        -d \"client_secret=$CLIENT_SECRET\" \\\n        -d \"client_id=simple-service\"')\n  \n  BGATES_ACCESS_TOKEN=$(echo $BGATES_TOKEN | jq -r .access_token)\n  ```\n\n- Call the endpoint `GET /api/private`:\n  ```\n  curl -i http://localhost:9080/api/private -H \"Authorization: Bearer $BGATES_ACCESS_TOKEN\"\n  ```\n\n  It should return:\n  ```\n  HTTP/1.1 200\n  bgates, it is private.\n  ```\n\n## Shutdown\n\n- To stop the `simple-service` application, go to the terminal where it is running and press `Ctrl+C`;\n- To stop and remove Docker Compose containers, network, and volumes, go to a terminal and, inside the `springboot-keycloak-openldap` root folder, run the following command:\n  ```\n  docker compose down -v\n  ```\n\n## Cleanup\n\nTo remove the Docker image create by this project, go to a terminal and, inside the `springboot-keycloak-openldap` root folder, run the following script:\n```\n./remove-docker-images.sh\n```\n\n## References\n\n- https://www.keycloak.org/docs/latest/server_admin/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivangfr%2Fspringboot-keycloak-openldap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivangfr%2Fspringboot-keycloak-openldap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivangfr%2Fspringboot-keycloak-openldap/lists"}