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-dev
Create 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 Server
4. 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/public
Protected 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
/user
endpoint:curl -H "Authorization: Bearer $TOKEN" http://localhost:8081/api/user
- Try accessing the
/admin
endpoint:curl -H "Authorization: Bearer $TOKEN" http://localhost:8081/api/admin
If your user doesn't have the
admin
role, 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: 似水流年