CODE WITH SIBIN

Solving Real Problems with Real Code


Spring Boot 3, Spring Security 6, and Keycloak Integration

This guide provides a comprehensive integration of Spring Boot 3Spring Security 6, and Keycloak (OpenID Connect/OAuth 2.0) for securing REST APIs. We'll cover:

  1. Keycloak Setup & Configuration
  2. Spring Boot 3 + Spring Security 6 Integration
  3. Role-Based Access Control (RBAC)
  4. Postman Testing Guide

1. Keycloak Setup & Configuration

1.1 Run Keycloak via Docker

docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:22.0.0 start-dev

Access Keycloak Admin Console: http://localhost:8080/admin

1.2 Configure Keycloak

  1. Create a Realm (springboot-realm)
  2. Create a Client (springboot-app)
    • Client ID: springboot-app
    • Root URL: http://localhost:8081 (Spring Boot app port)
    • Valid Redirect URIs: http://localhost:8081/*
    • Web Origins: http://localhost:8081
    • Access Type: confidential
    • Enable Client credentials & Authorization code flows.
  3. Create Roles (ROLE_ADMIN, ROLE_USER)
  4. Create Users & Assign Roles
    • User: user1 → Assign ROLE_USER
    • User: admin1 → Assign ROLE_ADMIN
  5. Get Client Secret
    • Go to Clients → springboot-app → Credentials → Copy Secret.

2. Spring Boot 3 + Spring Security 6 Integration

2.1 Dependencies (pom.xml)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2.2 Configure application.yml

server:
  port: 8081

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8080/realms/springboot-realm
          jwk-set-uri: http://localhost:8080/realms/springboot-realm/protocol/openid-connect/certs

2.3 Security Configuration

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> {}));
        return http.build();
    }
}

2.4 Controller with Role-Based Access

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class DemoController {

    @GetMapping("/user")
    @PreAuthorize("hasRole('ROLE_USER')")
    public String userEndpoint() {
        return "Hello, User!";
    }

    @GetMapping("/admin")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public String adminEndpoint() {
        return "Hello, Admin!";
    }

    @GetMapping("/public")
    public String publicEndpoint() {
        return "Public API";
    }
}

3. Postman Testing Guide

3.1 Get Access Token (Password Grant Flow)

Request:

POST http://localhost:8080/realms/springboot-realm/protocol/openid-connect/token
Headers:
- Content-Type: application/x-www-form-urlencoded
Body (form-data):
- grant_type: password
- client_id: springboot-app
- client_secret: [YOUR_CLIENT_SECRET]
- username: user1
- password: [USER_PASSWORD]
- scope: openid

Response:

{
    "access_token": "eyJhbGciOiJSUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 300
}

3.2 Access Protected API

Request:

GET http://localhost:8081/api/user
Headers:
- Authorization: Bearer [ACCESS_TOKEN]

Expected Response:

Hello, User!

3.3 Test Admin Endpoint (Should Fail for ROLE_USER)

Request:

GET http://localhost:8081/api/admin
Headers:
- Authorization: Bearer [ACCESS_TOKEN]

Expected Response:

{
    "error": "access_denied",
    "error_description": "Not authorized"
}

3.4 Public Endpoint (No Token Needed)

Request:

GET http://localhost:8081/api/public

Expected Response:

Public API

Next Steps

  • Refresh Tokens: Implement token refresh logic.
  • Custom Claims: Extract additional user info from JWT.

Leave a Reply

Your email address will not be published. Required fields are marked *