Skip to content

shirlenelss/SSO_keykloak

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Java SSO with Keycloak

A demonstration project showing Single Sign-On (SSO) implementation using Spring Boot applications and Keycloak as the identity provider.

Project Overview

This project contains:

  • Keycloak: Identity and access management server (port 8083)
  • App-A: Spring Boot application (port 8081)
  • App-B: Spring Boot application (port 8082)

Both applications use OAuth2/OpenID Connect to authenticate users via Keycloak SSO.

Architecture

┌─────────────┐
│  Keycloak   │
│  (port 8083)│
└──────┬──────┘
       │
       │ OAuth2/OIDC
       │
   ────┴────
   │       │
┌──▼───┐ ┌─▼────┐
│App-A │ │App-B │
│ 8081 │ │ 8082 │
└──────┘ └──────┘

Prerequisites

  • Java 17 or higher
  • Maven 3.6+
  • Docker and Docker Compose

Technology Stack

  • Spring Boot 3.x
  • Spring Security
  • Spring OAuth2 Client
  • Thymeleaf (for templates)
  • Keycloak 24.0

Quick Start

1. Start Keycloak

docker-compose up -d

Keycloak will be available at: http://localhost:8083

  • Admin username: admin
  • Admin password: admin

2. Configure Keycloak Realm and Clients

Create Realm

  1. Access Keycloak Admin Console at http://localhost:8083
  2. Login with admin credentials
  3. Click on the dropdown in the top-left (says "master")
  4. Click "Create Realm"
  5. Set Realm name: sso-demo
  6. Click "Create"

Create Client for App-A

  1. In the sso-demo realm, go to ClientsCreate client
  2. General Settings:
    • Client type: OpenID Connect
    • Client ID: app-a-client
    • Click Next
  3. Capability config:
    • Client authentication: ON
    • Authorization: OFF
    • Authentication flow: Check Standard flow and Direct access grants
    • Click Next
  4. Login settings:
    • Valid redirect URIs: http://localhost:8081/*
    • Valid post logout redirect URIs: http://localhost:8081/*
    • Web origins: http://localhost:8081
    • Click Save
  5. Go to Credentials tab
  6. Copy the Client secret and update it in app-a/src/main/resources/application.properties

Create Client for App-B

Repeat the same steps as App-A but with:

  • Client ID: app-b-client
  • Valid redirect URIs: http://localhost:8082/*
  • Valid post logout redirect URIs: http://localhost:8082/*
  • Web origins: http://localhost:8082
  • Update the client secret in app-b/src/main/resources/application.properties

Create Test User

  1. Go to UsersAdd user
  2. Set Username: testuser
  3. Click Create
  4. Go to Credentials tab
  5. Click Set password
  6. Set password (e.g., password)
  7. Turn off Temporary
  8. Click Save

3. Configure Applications

Update the client secrets in the application properties files:

app-a/src/main/resources/application.properties:

spring.security.oauth2.client.registration.keycloak.client-secret=YOUR_APP_A_CLIENT_SECRET

app-b/src/main/resources/application.properties:

spring.security.oauth2.client.registration.keycloak.client-secret=YOUR_APP_B_CLIENT_SECRET

4. Build and Run Applications

Using Maven

# Build the project
mvn clean install

# Run App-A (in one terminal)
cd app-a
mvn spring-boot:run

# Run App-B (in another terminal)
cd app-b
mvn spring-boot:run

Using Docker (if Dockerfiles are configured)

# Build and run with docker-compose
docker-compose up --build

Testing SSO

  1. Open browser and go to http://localhost:8081
  2. You'll be redirected to Keycloak login page
  3. Login with testuser / password
  4. You'll be redirected back to App-A and see your profile
  5. Open another tab and go to http://localhost:8082
  6. You should be automatically logged in (SSO!) without entering credentials again
  7. You'll see the same user profile in App-B

Application Endpoints

  • / - Profile page (protected, requires authentication)
  • /profile - User profile information
  • /public/** - Public resources (no authentication required)
  • /logout - Logout endpoint
  • / - Profile page (protected, requires authentication)
  • /profile - User profile information
  • /public/** - Public resources (no authentication required)
  • /logout - Logout endpoint

Logout Flow

How Logout Works

  1. User clicks logout link or navigates to /logout
  2. Application executes:
    • CustomLogoutHandler - Clears custom session data and JSESSIONID
    • SecurityContextLogoutHandler - Clears Spring Security context
  3. Session cookie (JSESSIONID) is invalidated
  4. User needs to login again to access protected resources

Expected Behavior

  • After logout, the session is completely cleared
  • Accessing any protected page will redirect to Keycloak login
  • Both applications maintain independent sessions with Keycloak

Important Notes

  • The logout is local to the application - it doesn't trigger Keycloak logout
  • For global SSO logout (logout from all apps), you would need to implement OIDC RP-Initiated Logout
  • Back-channel logout is disabled: .backChannelLogoutEnabled(false)

Configuration Details

OAuth2 Configuration

Both applications use the following OAuth2 settings:

# Client registration
spring.security.oauth2.client.registration.keycloak.client-id=<CLIENT_ID>
spring.security.oauth2.client.registration.keycloak.client-secret=<CLIENT_SECRET>
spring.security.oauth2.client.registration.keycloak.scope=openid,profile,email
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}

# Provider configuration
spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8083/realms/sso-demo
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

Security Configuration

The security configuration:

  • Allows public access to /public/** paths
  • Requires authentication for all other endpoints
  • Uses OAuth2 login with Keycloak
  • Implements custom logout with session clearing

Troubleshooting

Issue: "Unable to resolve Configuration with the provided Issuer"

Cause: Application cannot reach Keycloak at the issuer URI

Solution:

  • Ensure Keycloak is running: docker-compose ps
  • Check Keycloak is accessible: curl http://localhost:8083/realms/sso-demo/.well-known/openid-configuration
  • If using Docker for apps, use internal network name instead of localhost

Issue: Blank page after logout

Cause: No default page configured after logout

Solution:

  • The logout clears the session correctly
  • Navigate to / to trigger login again
  • Or add a logout success URL in SecurityConfig:
    .logout(logout -> logout
        .logoutSuccessUrl("/")
        // ...
    )

Issue: JSESSIONID not cleared

Cause: Session cookie settings or logout handlers not configured properly

Solution:

  • Verify CustomLogoutHandler invalidates the session
  • Verify SecurityContextLogoutHandler is added
  • Check browser dev tools to confirm cookie is removed after logout

Issue: "Invalid parameter: redirect_uri" from Keycloak

Cause: The redirect URI is not whitelisted in Keycloak client configuration

Solution:

  • Go to Keycloak Admin Console
  • Navigate to your client (app-a-client or app-b-client)
  • Add the redirect URI to Valid redirect URIs and Valid post logout redirect URIs
  • Make sure to include /* at the end (e.g., http://localhost:8081/*)

Issue: Cannot see login page, keeps showing blank page

Cause: Missing OAuth2 configuration or security misconfiguration

Solution:

  1. Check application.properties has correct OAuth2 settings
  2. Verify Keycloak realm and clients are created
  3. Check logs for errors: logging.level.org.springframework.security=DEBUG
  4. Ensure .oauth2Login(Customizer.withDefaults()) is in SecurityConfig

Issue: Login works but profile page is blank

Cause: Controller or template issues

Solution:

  • Verify ProfileController exists and is mapped correctly
  • Check profile.html template exists in src/main/resources/templates/
  • Verify Thymeleaf dependency is in pom.xml

Project Structure

java-sso-keykloak/
├── app-a/
│   ├── src/main/java/com/example/app1/
│   │   ├── App1Application.java          # Main application class
│   │   ├── SecurityConfig.java           # Spring Security configuration
│   │   ├── CustomLogoutHandler.java      # Custom logout handler
│   │   └── ProfileController.java        # Profile page controller
│   ├── src/main/resources/
│   │   ├── application.properties        # App-A configuration
│   │   └── templates/profile.html        # Profile page template
│   ├── Dockerfile                        # Docker configuration for App-A
│   └── pom.xml                           # Maven dependencies for App-A
├── app-b/
│   ├── src/main/java/com/example/app2/
│   │   ├── App2Application.java          # Main application class
│   │   ├── SecurityConfig.java           # Spring Security configuration
│   │   ├── CustomLogoutHandler.java      # Custom logout handler
│   │   └── ProfileController.java        # Profile page controller
│   ├── src/main/resources/
│   │   ├── application.properties        # App-B configuration
│   │   └── templates/profile.html        # Profile page template
│   ├── Dockerfile                        # Docker configuration for App-B
│   └── pom.xml                           # Maven dependencies for App-B
├── docker-compose.yml                    # Docker Compose for Keycloak
├── pom.xml                               # Parent POM
└── README.md                             # This file

Keycloak Data Persistence

Keycloak data is persisted in a Docker volume:

volumes:
  keycloak_data:
    driver: local

This ensures your realm, clients, and users are preserved across container restarts.

Security Considerations

⚠️ For Production Use:

  1. Change default passwords: Don't use admin/admin for Keycloak
  2. Use HTTPS: Configure SSL/TLS for all services
  3. Secure client secrets: Use environment variables or secret management
  4. Configure CORS properly: Restrict web origins to trusted domains
  5. Enable CSRF protection: Ensure CSRF tokens are validated
  6. Use secure session cookies: Configure Secure and HttpOnly flags
  7. Implement proper logout: Consider RP-Initiated Logout for global logout

Additional Resources

License

This is a demonstration project for educational purposes.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors