Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .env-example
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
#
# You can generate your own key pair
#
PUBLIC_KEY='-----BEGIN PUBLIC KEY-----MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt8FNmx/af8Ul2eo+dwL/JjnC2cJGhjSMT0AmM3NpGEPNuBC7gPtaMiPMkreoZ9aL5wD9M/Cz1jWdQdj5fT1nxSDut+9Wm+stqAyF5pkw4hXN9Qqhi048sZsVsmqi8M4XJqOg2W2a9+CvQPU1jvSQuwJEEFoXfnPvtVs6aB/YcUoHNuBUlYGVjcZonkJz3JByfn5+q/gI4hsTXzHSuL81n6Uwr2cL8K9DbC5Q8jICK+p8rNZy3z9FuGFtt4f5MG5lVtI8TlTrzhoZ6mKsGaeV+6a6jSFAK4uunGpowfoOKO2aS3GBp6sHz3kEY2vWomr5qvN5z7YSJm0kmtA4fp77MkXuezVLPfjc0G26fTtOoreLKCgFd9YsfacX/Q4nVZv8K5lZ+qQBhX5fl+k3qa8NnDsyGmJnj0UPwbKkdRpRpWPfmc5cnVYkEaQZ/D/St0UvdiqaVUhPyEMMKAYMa7PbQe6L2MQmA3ZsECnbksn6ZF9KWkK/slqOzAhKVo/r7d9X1wT/uVt/b0fBriwGIkORtnQtRQFdslbjrUXDt2GWk3/tm9OHBn/g/jK+NK4TyTzuiXawo39gg/CWZ0MQFU6hLXdn15244Nef2mIWbJece6CLwF/Gn43+z+SuPop+zG1JmkReJ8FDiEjq8KRLNf9UHRFXITsKThsr5s/4+jBTPJkCAwEAAQ==-----END PUBLIC KEY-----'
PRIVATE_KEY='-----BEGIN PRIVATE KEY-----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-----END PRIVATE KEY-----'
SPRING_PROFILES_ACTIVE=development
27 changes: 27 additions & 0 deletions HealthCheck
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Arrays;

class HealthCheck {

static final int OK = 0;
static final int NOK = -1;

public static void main(String[] args) {
try {
System.exit(doHealthCheck(URI.create(Arrays.stream(args).findFirst().orElseThrow())));
} catch (Exception ignored) {
// Just quietly exit with NOK
System.exit(NOK);
}
}

private static int doHealthCheck(URI uri) throws IOException {
if (!(uri.toURL().openConnection() instanceof HttpURLConnection httpConnection)) {
return NOK;
}

return HttpURLConnection.HTTP_OK == httpConnection.getResponseCode() ? OK : NOK;
}
}
15 changes: 2 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,6 @@

### Specifying Environment Variables

There are two mandatory environment variables that you need to set before running the service:

| Variable Name | Description | Remarks |
|---------------|------------------------------------------------------------------------|-------------------------------------|
| `PUBLIC_KEY` | The public key used to verify JWT authorization | RSA public key (2048 bits or more) |
| `PRIVATE_KEY` | The private key used to generate access tokens for authenticated users | RSA private key (2048 bits or more) |

> [!TIP]
>
> You can visit [this link](https://travistidwell.com/jsencrypt/demo/) to generate a pair of RSA keys for testing purposes.

The `SPRING_PROFILES_ACTIVE` variable is optional, but you can set it to `development` for local development.

You can either specify these environment variables in a `.env` file or set them directly in Run Configurations.
Expand All @@ -51,11 +40,11 @@ An example `.env` file can be copied from [this file](/.env-example).

### Running the Required Containers

You can run [this script (Windows only)](/run-docker.cmd) and it will start the required containers for local development: PostgreSQL and Redis.
You can run [this script (Windows only)](/initialize-postgres-keycloak.cmd) and it will start the required containers for local development: PostgreSQL and KeyCloak.

## Running the Compose Stack

You can run [this script](/run-docker.cmd) and it will build the service image and start the containers for you.
You can run [this script(Windows only)](/run-docker-compose-stack.cmd) and it will build the service image and start the containers for you.

# Additional Notes

Expand Down
39 changes: 19 additions & 20 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,17 @@ services:
context: .
dockerfile: ./Dockerfile
ports:
- "8080:8080"
- "8088:8088"
environment:
- SPRING_PROFILES_ACTIVE=development
- DATABASE_HOST=postgresql
- DATABASE_USERNAME=postgres
- REDIS_HOST=redis
# Those keys are randomly generated, please replace them if you can
- PUBLIC_KEY=-----BEGIN PUBLIC KEY-----MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArEl0hMdt0ozeuilVp+5qfjWUdLieuB8F1LTgeYSfLg6W2EYLQ0mLl498h3fCOCnChWc+Rxb0PbUfPyO61pZvQp5822RD53t1Qvued9lmFti7HITlPjPYXMk2MPCMQAE7AR77VMdNZfiFUXqy9T+GKVmkrZe1c8Zu17Qtm4rjto1nbbHJDHB6FSfdWGHDdJeK696b8IaZ+Zu4nySTs5foTvfHZxNzemkOMsqStsWABTtkuK3vvfDJlFZUvZQSDSM+APqSfBmcdSa5dPSK8Pm5mRfpH//SYVk0XK6xKT4iuP1cXATmYtc1A05IHw+dAzzarbRK9uIRkV5wYa8D6WPRGP7jdOXDkFgqg9tUAOJ6gnp3OP2KotTAyzkfhtydTqEiOWsUITZ5eb2Q5FnLqV1pGqJXpOak/fw5U/kXorzPy4dM2Iwr1XQRjOTfWk9xzqBLXzSapwHCx1TSnXYtDbMDMprlGcNLtCm0MjzMnykqljRe0l3EGh44A14dx2PJVzaRVyj2+liLZTLgb89YFvwaR8gGaz01YXWUb3u3MAwr8tgmONjTcF+F9epfwB6OOad2mx2Y5svu7yc01vMP+5E5D/lkYIlUcKUk9CUlp1jZB3AqUOXFPVjgYRxGHc92/55fyMc4f23wqWj5FHsBvVljPKlhT/dUEH91IPh6YBKClMcCAwEAAQ==-----END PUBLIC KEY-----
- PRIVATE_KEY=-----BEGIN PRIVATE KEY-----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-----END PRIVATE KEY-----
- SERVER_PORT=8088
- KEYCLOAK_HOST=http://keycloak:8080
depends_on:
postgresql:
condition: service_healthy
redis:
keycloak:
condition: service_healthy
postgresql:
networks:
Expand All @@ -44,29 +42,30 @@ services:
interval: 10s
timeout: 5s
retries: 5
redis:
keycloak:
networks:
- spring-base-network
image: 'redis:8.2.3-bookworm'
container_name: redis
image: quay.io/keycloak/keycloak:26.4
container_name: standalone-keycloak
command: start-dev
environment:
- KC_BOOTSTRAP_ADMIN_USERNAME=admin
- KC_BOOTSTRAP_ADMIN_PASSWORD=123456
- KC_HEALTH_ENABLED=true
- KC_METRICS_ENABLED=true
- KC_HOSTNAME=keycloak
ports:
- "6379:6379"
volumes:
- redis-volume:/data
- "8080:8080"
- "9000:9000"
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { java.net.URI uri = java.net.URI.create(args[0]); System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)uri.toURL().openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java /tmp/HealthCheck.java http://localhost:9000/health/live']
interval: 5s
timeout: 5s
retries: 5
command:
- redis-server
- --save 60 1
- --loglevel warning
retries: 15
start_period: 10s
volumes:
postgres-volume:
name: postgres-volume
redis-volume:
name: redis-volume
networks:
spring-base-network:
driver: bridge
119 changes: 119 additions & 0 deletions initialize-postgres-keycloak.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
@echo off
SETLOCAL EnableDelayedExpansion

docker info >nul 2>&1
if errorlevel 1 (
echo Error: Docker daemon is not running.
echo Please start Docker Desktop or Docker service and run this script again.
exit /b 1
)

:: --- PostgreSQL Setup ---
SET PG_CONTAINER_NAME=standalone-postgresql
SET PG_VOLUME_NAME=postgres-volume
SET PG_COMMAND=docker run -d --name !PG_CONTAINER_NAME! -e "POSTGRES_USER=postgres" -e "POSTGRES_PASSWORD=123456" -e "POSTGRES_DB=myspringdatabase" -p 5432:5432 -v !PG_VOLUME_NAME!:/var/lib/postgresql/data postgres:18.0-alpine3.22

echo Checking PostgreSQL container [%PG_CONTAINER_NAME%]...
docker ps -a | findstr /C:"!PG_CONTAINER_NAME!" >nul
if errorlevel 1 (
echo Container [%PG_CONTAINER_NAME%] not existed, creating with volume [%PG_VOLUME_NAME%]...
!PG_COMMAND!
) else (
docker ps | findstr /C:"!PG_CONTAINER_NAME!" >nul
if errorlevel 1 (
echo Container with the same name [%PG_CONTAINER_NAME%] stopped, restarting...
docker start !PG_CONTAINER_NAME!
) else (
echo Container [%PG_CONTAINER_NAME%] is already running...
)
)

:: KEYCLOAK_REALM and CLIENT_ID should match the values in application.properties

:: --- Keycloak Setup ---
set KEYCLOAK_IMAGE=quay.io/keycloak/keycloak:26.4
set KEYCLOAK_CONTAINER=standalone-keycloak

:: application-properties.security.realm-name
set KEYCLOAK_REALM=spring-base

:: application-properties.security.client-name
set CLIENT_ID=spring-base-client

set KEYCLOAK_OVERLORD=admin
set KEYCLOAK_ADMIN_PASSWORD=123456

echo Stopping and removing old Keycloak containers/images...
docker container rm -f %KEYCLOAK_CONTAINER% 2>nul

echo Starting Keycloak container [%KEYCLOAK_CONTAINER%]...
docker run --name %KEYCLOAK_CONTAINER% --detach -p 8080:8080 -p 9000:9000 -e "KC_BOOTSTRAP_ADMIN_USERNAME=%KEYCLOAK_OVERLORD%" -e "KC_BOOTSTRAP_ADMIN_PASSWORD=%KEYCLOAK_ADMIN_PASSWORD%" -e "KC_HEALTH_ENABLED=true" -e "KC_METRICS_ENABLED=true" %KEYCLOAK_IMAGE% start-dev

docker cp HealthCheck %KEYCLOAK_CONTAINER%:/tmp/HealthCheck.java

:health_check
docker exec %KEYCLOAK_CONTAINER% sh -c "java /tmp/HealthCheck.java http://localhost:9000/health/live"
if !ERRORLEVEL!==0 (
echo Keycloak is serviceable!
goto after_health_check
) else (
echo Waiting for Keycloak to be healthy...
timeout /t 5 >nul
goto health_check
)

:after_health_check

set KCADM_PATH=/opt/keycloak/bin/kcadm.sh

echo Configuring KCADM credentials...
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% config credentials --server http://localhost:8080 --realm master --user %KEYCLOAK_OVERLORD% --password %KEYCLOAK_ADMIN_PASSWORD%

docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% create realms -s realm=%KEYCLOAK_REALM% -s enabled=true

echo Creating client [%CLIENT_ID%]...

:: if anyone has any better idea to not use variable, let me know
set "CLIENT_CREATE_CMD=docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% create clients -r %KEYCLOAK_REALM% -s clientId=%CLIENT_ID% -s enabled=true -s publicClient=true -s directAccessGrantsEnabled=true -i"
for /f "usebackq delims=" %%i in (`!CLIENT_CREATE_CMD!`) do (
set CLIENT_UUID=%%i
)

:: --- Role and User Setup ---
SET ROLE_ADMIN=ADMIN
SET ROLE_POWER_USER=POWER_USER
SET ROLE_USER=USER

set ADMIN_USERNAME=administrator
set POWER_USER_USERNAME=power_user
set USER_USERNAME=user

docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% create clients/%CLIENT_UUID%/roles -r %KEYCLOAK_REALM% -s name=%ROLE_ADMIN%
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% create clients/%CLIENT_UUID%/roles -r %KEYCLOAK_REALM% -s name=%ROLE_POWER_USER%
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% create clients/%CLIENT_UUID%/roles -r %KEYCLOAK_REALM% -s name=%ROLE_USER%

echo Creating admin user [%ADMIN_USERNAME%]...
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% create users -r %KEYCLOAK_REALM% -s username=%ADMIN_USERNAME% -s enabled=true -s email=administrator@email.com -s firstName=Administrator -s lastName=User
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% set-password -r %KEYCLOAK_REALM% --username %ADMIN_USERNAME% --new-password 123456

echo Assigning client role [%ROLE_ADMIN%] to user [%ADMIN_USERNAME%] in client [%CLIENT_ID%]...
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% add-roles -r %KEYCLOAK_REALM% --uusername %ADMIN_USERNAME% --cclientid %CLIENT_ID% --rolename %ROLE_ADMIN%
echo Created user [%ADMIN_USERNAME%] with password 123456 and client role [%ROLE_ADMIN%] in client [%CLIENT_ID%]

echo Creating regular user [%USER_USERNAME%]...
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% create users -r %KEYCLOAK_REALM% -s username=%USER_USERNAME% -s enabled=true -s email=user@email.com -s firstName=Normal -s lastName=User
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% set-password -r %KEYCLOAK_REALM% --username %USER_USERNAME% --new-password 123456

echo Assigning client role [%ROLE_USER%] to user [%USER_USERNAME%] in client [%CLIENT_ID%]...
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% add-roles -r %KEYCLOAK_REALM% --uusername %USER_USERNAME% --cclientid %CLIENT_ID% --rolename %ROLE_USER%
echo Created user [%USER_USERNAME%] with password 123456 and client role [%ROLE_USER%] in client [%CLIENT_ID%]

echo Creating power user [%POWER_USER_USERNAME%]...
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% create users -r %KEYCLOAK_REALM% -s username=%POWER_USER_USERNAME% -s enabled=true -s email=power_user@email.com -s firstName=Power -s lastName=User
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% set-password -r %KEYCLOAK_REALM% --username %POWER_USER_USERNAME% --new-password 123456

echo Assigning client role [%ROLE_POWER_USER%] to user [%POWER_USER_USERNAME%] in client [%CLIENT_ID%]...
docker exec %KEYCLOAK_CONTAINER% %KCADM_PATH% add-roles -r %KEYCLOAK_REALM% --uusername %POWER_USER_USERNAME% --cclientid %CLIENT_ID% --rolename %ROLE_POWER_USER%
echo Created user [%POWER_USER_USERNAME%] with password 123456 and client role [%ROLE_POWER_USER%] in client [%CLIENT_ID%]

ENDLOCAL
Loading