Use OIDC and keycloak to protect your API
Table of Contents
						
						1. Setup Keycloak
Before you start coding, make sure you have a running Keycloak instance:
Run Keycloak using Docker
docker run -d --name keycloak -p 8080:8080 \
    -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin \
    quay.io/keycloak/keycloak:latest start-devCreate a Realm and Client
- Open Keycloak at http://localhost:8080/
- Login with admin/admin
- Create a new Realm (e.g., myrealm)
- Under the Realm, create a Client:
- Client ID: myclient
- Client Type: OpenID Connect
- Root URL: http://localhost:8080
- Click Save
 
- Client ID: 
- Under Client Authentication, set:- Access Type: confidential
- Enable Service Accounts
- Save the Client Secret (You'll use this in Spring Boot)
 
- Access Type: 
- Create a new User:
- Go to Users > Create
- Set username/password (testuser/testpassword)
- Assign a role (user)
 
- Go to 
2. Spring Boot Project Setup
Add Dependencies (Maven)
Add the required dependencies for Keycloak and Spring Security:
<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Security with OAuth2 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
    </dependency>
    <!-- Spring Boot Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- Keycloak Spring Security Adapter -->
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-spring-boot-starter</artifactId>
        <version>22.0.1</version>
    </dependency>
</dependencies>3. Configure application.yml
server:
  port: 8081  # Run on a different port than Keycloak
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8080/realms/myrealm
  application:
    name: my-secure-app
keycloak:
  realm: myrealm
  auth-server-url: http://localhost:8080
  resource: myclient
  credentials:
    secret: YOUR_CLIENT_SECRET
  bearer-only: true  # This means it will act as a Resource Server4. Secure API Endpoints
Create a secured REST API with role-based access:
package com.example.demo.controller;
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 SecureController {
    @GetMapping("/public")
    public String publicEndpoint() {
        return "This is a public endpoint";
    }
    @GetMapping("/user")
    @PreAuthorize("hasAuthority('ROLE_user')")
    public String userEndpoint() {
        return "Hello, User! You have access to this protected endpoint.";
    }
    @GetMapping("/admin")
    @PreAuthorize("hasAuthority('ROLE_admin')")
    public String adminEndpoint() {
        return "Hello, Admin! You have elevated access.";
    }
}5. Configure Security
Create a security config class:
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public").permitAll()
                .requestMatchers("/api/user").hasRole("user")
                .requestMatchers("/api/admin").hasRole("admin")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())));
        return http.build();
    }
    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); // Keycloak uses roles without "ROLE_" prefix
        grantedAuthoritiesConverter.setAuthoritiesClaimName("realm_access.roles");
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }
}6. Test the Security
Public API (No Token Needed)
curl http://localhost:8081/api/publicProtected API (Requires Token)
- Get an access token from Keycloak:
export TOKEN=$(curl -X POST 'http://localhost:8080/realms/myrealm/protocol/openid-connect/token' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'client_id=myclient' \ -d 'client_secret=YOUR_CLIENT_SECRET' \ -d 'username=testuser' \ -d 'password=testpassword' \ -d 'grant_type=password' | jq -r .access_token)
- Access the protected /userendpoint:curl -H "Authorization: Bearer $TOKEN" http://localhost:8081/api/user
- Try accessing the /adminendpoint:curl -H "Authorization: Bearer $TOKEN" http://localhost:8081/api/adminIf your user doesn't have the adminrole, it should return 403 Forbidden.
Conclusion
This setup integrates Spring Boot with Keycloak using OIDC to protect APIs:
- Keycloak is the authentication provider.
- Spring Security handles authentication via JWT.
- Role-based access is configured via @PreAuthorize.
Would you like help setting up a frontend client for login? 🚀
Comments |0|
Category: 似水流年