Keycloak එක්ක Spring Boot API ආරක්ෂා කරමු | Secure REST APIs with Keycloak

අද කාලේ software ලෝකේ හැමතැනම REST APIs නේද මචන්? Mobile app එකක් ගත්තත්, web app එකක් ගත්තත්, microservices අතරේ communication ගත්තත්, APIs නැතුව බෑ. එහෙමනම් මේ APIs ආරක්ෂා කරගන්න එකත් අත්යවශ්යයි. අපේ sensitive data, business logic එක protect කරගන්න මේක හරියටම කරන්න ඕනේ. අද අපි කතා කරන්නේ Spring Boot REST APIs Keycloak එක්ක කොහොමද ආරක්ෂා කරගන්නේ කියලා. මේක පට්ට වැදගත් subject එකක් ඔයාලගේ project වලටත්!
API ආරක්ෂාව මොකටද?
කට්ටිය හිතයි APIs හැදුවා, වැඩ කරනවා නේද කියලා. ඒත් එහෙම නෑ. Attackers ලා හැම වෙලාවෙම බලන් ඉන්නේ දුර්වල තැන් හොයාගන්න. Unauthorized access, data breaches, injections වගේ ප්රශ්න ඕන තරම් එන්න පුළුවන්. ඒ නිසා, authentication (කවුද මේ access කරන්නේ?), authorization (එයාට මොනවද කරන්න පුළුවන්?) කියන දේවල් හරියට manage කරන්න ඕනේ. අපේ Spring Boot backend එකට එන requests වලට කලින්, කවුද මේ request එක එවන්නේ කියලා හරියටම identify කරලා, එයාට මේ endpoint එක access කරන්න පුලුවන්ද කියලා බලන්න ඕනේ.
Keycloak කියන්නේ මොකක්ද?
Keycloak කියන්නේ Open Source Identity and Access Management (IAM) solution එකක්. සරලව කිව්වොත්, user authentication, authorization, single sign-on (SSO) වගේ දේවල් වලට ගොඩක්ම පහසුකම් සපයන tool එකක්. Keycloak වලට පුළුවන් user account manage කරන්න, different applications වලට authentication provide කරන්න, සහ JWT tokens වගේ දේවල් generate කරන්න. අපිට පුළුවන් Keycloak අපේ Spring Boot application එකට resource server එකක් විදියට integrate කරලා, authentication process එක එයාලට බාර දෙන්න.
JWT වල රහස
මුලින්ම JWT (JSON Web Token) කියන්නේ මොකක්ද කියලා පොඩ්ඩක් බලමු. මේක තමා මේ හැම දේටම පදනම. JWT එකක් කියන්නේ compact, URL-safe way එකක් party දෙකක් අතරේ claims ආරක්ෂිතව transmit කරන්න.
JWT එකක කොටස් තුනක් තියෙනවා:
- Header: Token එකේ වර්ගය (JWT) සහ signature එක හදන්න පාවිච්චි කරන algorithm එක (උදා: HS256, RS256) තියෙනවා.
- Payload: මේක තමයි ඇත්තටම claims තියෙන තැන. Claims කියන්නේ user ට අදාළ විස්තර (user ID, username, roles, permissions) වගේ දේවල්. මේවා standard claims (iss, exp, sub) වෙන්නත් පුළුවන්, custom claims වෙන්නත් පුළුවන්.
- Signature: Header එක සහ Payload එක base64url-encode කරලා, secret key එකක් හෝ private key එකක් පාවිච්චි කරලා cryptographic signature එකක් හදනවා. මේකෙන් token එක tampering වලින් ආරක්ෂා වෙනවා.
API call එකක් එනකොට, Spring Boot backend එකට එන්නේ Authorization Header එකේ bearer token එකක් විදියට මේ JWT එක. Spring Security framework එක මේ JWT එක validate කරලා, ඒකේ තියෙන user identity සහ roles extract කරගෙන, අපේ API endpoint එකට access දෙන්න පුලුවන්ද කියලා තීරණය කරනවා.
Spring Boot එක්ක Keycloak පුරුදු කරගමු
හරි, දැන් අපි බලමු කොහොමද මේක Spring Boot project එකකට දාගන්නේ කියලා.
පියවර 1: Keycloak Setup
මුලින්ම Keycloak server එකක් run කරගන්න ඕනේ. Docker එකෙන් පහසුවෙන් කරන්න පුළුවන්. Keycloak server එකේ Realm
එකක් හදන්න, Client
එකක් register කරන්න (public client), Role
එකක් (ex: admin
, user
) හදලා user කෙනෙක්ට assign කරන්න ඕනේ.
පියවර 2: Maven Dependencies
ඔයාලගේ pom.xml
එකට මේ dependencies add කරගන්න. Spring Boot 3.x නම්, Spring Security 6.x එක්ක Keycloak Adapter එක direct use කරන්න බෑ. ඒ වෙනුවට spring-security-oauth2-resource-server
සහ spring-security-oauth2-jose
භාවිතා කරනවා.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
පියවර 3: application.yml Configuration
application.yml
(හෝ application.properties
) එකේ Keycloak server එකේ details configure කරන්න ඕනේ.
spring:
security:
oauth2:
resourceserver:
jwt:
# Keycloak realm public key endpoint URL
# Replace with your Keycloak server URL, realm name
jwk-set-uri: http://localhost:8080/realms/your-realm-name/protocol/openid-connect/certs
# If you need to map Keycloak roles to Spring Security authorities
# issuer-uri: http://localhost:8080/realms/your-realm-name
jwk-set-uri
එකෙන් තමා Spring Boot Keycloak server එකෙන් public key එක retrieve කරගෙන JWT signature එක validate කරන්නේ.
පියවර 4: Spring Security Configuration
දැන් අපිට Spring Security configure කරන්න ඕනේ. SecurityFilterChain
bean එකක් හදලා requests වලට security rules දාන්න ඕනේ.
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.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // To enable @PreAuthorize and @PostAuthorize
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // Disable CSRF for stateless REST APIs
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public/**").permitAll() // Allow public access to certain paths
.anyRequest().authenticated() // All other requests require authentication
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwtAuthenticationConverter(jwtAuthenticationConverter()) // Custom converter for roles
)
);
return http.build();
}
// Configure how roles from JWT are mapped to Spring Security authorities
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
// By default, Spring Security looks for 'scope' or 'scp' claims for authorities.
// Keycloak typically puts roles in a 'realm_access.roles' or 'resource_access.<client>.roles' claim.
// We need to configure the converter to look for Keycloak's 'roles' claim.
// If your Keycloak realm roles are in 'realm_access.roles':
grantedAuthoritiesConverter.setAuthoritiesClaimName("realm_access.roles");
// If your Keycloak client roles are in 'resource_access.<client-id>.roles':
// grantedAuthoritiesConverter.setAuthoritiesClaimName("resource_access.your-client-id.roles");
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); // Add 'ROLE_' prefix
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
}
JwtAuthenticationConverter
එක හරහා අපිට පුළුවන් JWT එකේ තියෙන roles Spring Security authorities විදියට map කරගන්න. Keycloak JWT එකේ realm_access.roles
(realm roles) හෝ resource_access.<client-id>.roles
(client roles) කියන claims ඇතුළේ roles තියෙනවා. අපිට ඕනේ මේවා ROLE_ADMIN
, ROLE_USER
වගේ Spring Security format එකට convert කරගන්න.
Endpoint ආරක්ෂා කරමු
දැන් ඔයාලට පුළුවන් Controller
එකේ methods වලට @PreAuthorize
annotation එක දාලා endpoint ආරක්ෂා කරන්න.
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MySecuredController {
@GetMapping("/public/hello")
public String publicHello() {
return "Hello from public API!";
}
@GetMapping("/user/hello")
@PreAuthorize("hasRole('USER')") // Only users with 'USER' role can access
public String userHello() {
return "Hello from user API! You have USER role.";
}
@GetMapping("/admin/hello")
@PreAuthorize("hasRole('ADMIN')") // Only users with 'ADMIN' role can access
public String adminHello() {
return "Hello from admin API! You have ADMIN role.";
}
@GetMapping("/any/hello")
@PreAuthorize("isAuthenticated()") // Any authenticated user can access
public String anyAuthenticatedHello() {
return "Hello from authenticated API! You are logged in.";
}
}
මේකෙන් වෙන්නේ, /user/hello
endpoint එකට request එකක් එනකොට, Spring Security token එක validate කරලා, ඒකේ තියෙන roles බලනවා. USER
role එක තියෙනවා නම් විතරයි access දෙන්නේ. නැත්නම් 403 Forbidden
error එකක් ලැබෙනවා.
ඕන නම් ඔයාලට hasAuthority('SCOPE_read')
වගේ permission-based authorization වලටත් යන්න පුළුවන්. ඒකට Keycloak client එකේ scopes define කරලා, JWT එකට ඒ scopes add වෙන්න configure කරන්න ඕනේ.
නිගමනය
ඔන්න එහෙනම් කට්ටියටම Spring Boot REST APIs Keycloak එක්ක කොහොමද secure කරන්නේ කියලා පැහැදිලි වෙන්න ඇති. JWT වල රහස, Spring Security එක්ක කොහොමද මේවා වැඩ කරන්නේ, ඒ වගේම Keycloak integrate කරන හැටිත් අපි බැලුවා.
API security කියන්නේ simply authentication and authorization විතරක් නෙවෙයි, මේක continuous process එකක්. අලුත් threats එනකොට, ඒවට adjust වෙන්නත් ඕනේ.
මේක ඔයාලගේ project වලටත් පාවිච්චි කරලා බලන්න. මොකක් හරි ප්රශ්නයක් ආවොත්, තව දෙයක් දැනගන්න ඕන නම්, comment section එකේ අහන්න. අපි උදව් කරන්නම්. Happy coding මචන්ලා!