Keycloak RBAC සමඟ Spring Security සුරක්ෂිතභාවය: ගැඹුරු මාර්ගෝපදේශයක්

Keycloak RBAC සමඟ Spring Security සුරක්ෂිතභාවය: ගැඹුරු මාර්ගෝපදේශයක්

ආයුබෝවන් යාළුවනේ, කොහොමද ඉතින්?

අපි හැමෝම දන්නවා අපේ software applications හදනකොට ආරක්ෂාව කියන්නේ නැතුවම බැරි දෙයක් කියලා. පොඩි application එකක් වුණත්, ලොකු enterprise system එකක් වුණත්, user කෙනෙකුට තියෙන්න ඕන access level එක හරියට කළමනාකරණය කරන එක හරිම වැදගත්. නැත්නම් ඩේටා එක අවුල් වෙන්න පුළුවන්, සමහර වෙලාවට ලොකු හානි වෙන්නත් පුළුවන්.

අද අපි කතා කරන්න යන්නේ මේ security පැත්තේ තියෙන හරිම වැදගත් concept එකක් ගැන – ඒ තමයි Fine-Grained Authorization, විශේෂයෙන්ම Role-Based Access Control (RBAC). මේක Implement කරන්න ලෝකේ පුරාම ගොඩක් අය පාවිච්චි කරන බලසම්පන්න Identity and Access Management (IAM) විසඳුමක් තමයි Keycloak. අපි බලමු Keycloak භාවිතයෙන් අපේ Spring Boot applications කොහොමද මේ RBAC හරහා ආරක්ෂා කරගන්නේ කියලා, ඒ වගේම methods සහ URLs ආරක්ෂා කරන්න Spring Security වල @PreAuthorize වගේ annotations කොහොමද පාවිච්චි කරන්නේ කියලත්.

ඉතින්, අපි පටන් ගමු!

RBAC සහ Keycloak හැඳින්වීම

Role-Based Access Control (RBAC) කියන්නේ මොකක්ද?

සරලව කිව්වොත්, RBAC කියන්නේ user කෙනෙකුට application එකක ඇතුළට වෙන්න පුළුවන් දේවල් (permissions) තීරණය කරන්නේ, ඒ user ට දීලා තියෙන role එක අනුවයි. User කෙනෙකුට එක role එකක් හෝ කිහිපයක් තියෙන්න පුළුවන්. උදාහරණයක් විදිහට:

  • Admin Role එකක් තියෙන කෙනෙකුට system එකේ හැම දේටම access තියෙන්න පුළුවන්. (data delete කරන්න, users manage කරන්න)
  • User Role එකක් තියෙන කෙනෙකුට තියෙන්නේ view-only access එකක් වෙන්න පුළුවන්. (data බලන්න විතරක්)
  • Manager Role එකක් තියෙන කෙනෙකුට data approve කරන්න පුළුවන් වෙන්න පුළුවන්.

මේ ක්‍රමයෙන් security management එක ගොඩක් ලේසි වෙනවා. User කෙනෙක් අලුතෙන් ආවම අපි කරන්නේ එයාලට අදාල role එක assign කරන එක විතරයි. එච්චරයි! ඒක තමයි RBAC වල ලොකුම වාසිය.

Keycloak කියන්නේ මොකක්ද?

Keycloak කියන්නේ Red Hat ආයතනයෙන් නඩත්තු කරන open-source Identity and Access Management (IAM) විසඳුමක්. මේක Single Sign-On (SSO) විදිහටත්, OpenID Connect (OIDC) සහ OAuth 2.0 වගේ industry standards පාවිච්චි කරලා secure authentication සහ authorization ලබා දෙන්නත් පුළුවන්. Keycloak වල ප්‍රධානම කොටස් ටිකක් ගැන බලමු:

  • Realms: Keycloak වල security domain එකක්. Application sets කිහිපයක් වෙන් කරලා manage කරන්න මේක පාවිච්චි කරනවා. උදාහරණයක් විදිහට, company එකේ HR system එකට වෙන realm එකක්, Sales system එකට වෙන realm එකක්.
  • Clients: Application එකක් හෝ සේවාවක්. මේවා තමයි Keycloak එකෙන් authentication සහ authorization ගන්න අය. (e.g., your Spring Boot application).
  • Users: System එකට log වෙන individuals.
  • Roles: User කෙනෙක්ට තියෙන්න පුළුවන් access permissions group එකක්. Keycloak වල roles වර්ග දෙකක් තියෙනවා:
    • Realm Roles: මුළු realm එකටම අදාලයි. මේවා සාමාන්‍යයෙන් user ගේ general capabilities (e.g., admin, user) විස්තර කරනවා.
    • Client Roles: යම්කිසි client (application) එකකට විතරක් අදාලයි. මේවා application-specific permissions (e.g., product_viewer, order_creator) විස්තර කරන්න පාවිච්චි කරනවා.

Keycloak හි Roles නිර්මාණය සහ කළමනාකරණය

මුලින්ම Keycloak server එකක් install කරගෙන run කරගන්න ඕනේ. Docker පාවිච්චි කරනවා නම් හරිම ලේසියි.

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

දැන් http://localhost:8080 වලට ගිහින් Keycloak admin console එකට log වෙන්න පුළුවන්. සාමාන්‍යයෙන් default master realm එකක් තියෙනවා. අපේ application එකට අලුත් realm එකක් හදාගමු. උදාහරණයක් විදිහට my-app-realm කියලා.

Realm Roles නිර්මාණය කිරීම

Admin console එකේ my-app-realm තෝරලා, වම් පැත්තේ menu එකෙන් Roles වලට ගිහින් අලුත් role එකක් හදන්න පුළුවන්. අපි APP_ADMIN සහ APP_USER කියලා roles දෙකක් හදමු.

  • Name: APP_ADMIN
  • Name: APP_USER

Client සහ Client Roles නිර්මාණය කිරීම

දැන් අපේ Spring Boot application එකට client එකක් හදාගමු. වම් පැත්තේ menu එකෙන් Clients වලට ගිහින් Create client click කරන්න.

  • Client ID: spring-boot-app
  • Client authentication: Off (Public client එකක් නිසා)
  • Authorization: Off
  • Root URL: http://localhost:8081 (ඔබේ Spring Boot application එක run වෙන port එක)
  • Valid redirect URIs: http://localhost:8081/*
  • Web origins: http://localhost:8081

Client එක හදලා ඉවර වුණාට පස්සේ, ඒ client එකේ Roles tab එකට ගිහින් client roles හදන්න පුළුවන්. අපි PRODUCT_VIEWER සහ PRODUCT_EDITOR කියලා roles දෙකක් හදමු.

  • Name: PRODUCT_VIEWER
  • Name: PRODUCT_EDITOR

Users සහ Role Assignment

දැන් Users වලට ගිහින් අලුත් user කෙනෙක් හදාගමු. උදාහරණයක් විදිහට john.doe කියලා. එයාලට password එකකුත් set කරන්න. ඊට පස්සේ ඒ user ගේ Role Mappings tab එකට ගිහින් roles assign කරන්න පුළුවන්. Client Roles dropdown එකෙන් spring-boot-app client එක තෝරලා, available roles වලින් PRODUCT_VIEWER සහ PRODUCT_EDITOR (හෝ වෙන ඕනෑම එකක්) Assigned Roles වලට add කරන්න. ඒ වගේම Realm Roles වලිනුත් APP_ADMIN හෝ APP_USER assign කරන්න පුළුවන්. මේ දෙකම Spring Security වලට map වෙන හැටි අපි බලමු.

Spring Boot Application එක Keycloak සමඟ සම්බන්ධ කිරීම

Dependencies Add කිරීම

මුලින්ම Spring Boot project එකකට Keycloak adapter dependencies add කරගන්න ඕනේ. Maven පාවිච්චි කරනවා නම් pom.xml එකට මේ ටික add කරන්න.

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>24.0.2</version> <!--  Keycloak අනුවාදය අනුව වෙනස් විය හැක -->
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

application.properties Configuration

දැන් src/main/resources/application.properties (හෝ application.yml) file එකට Keycloak configuration එක add කරන්න.

# Keycloak Configuration
keycloak.auth-server-url=http://localhost:8080/
keycloak.realm=my-app-realm
keycloak.resource=spring-boot-app
keycloak.public-client=true
keycloak.use-resource-role-mappings=true # Client roles පාවිච්චි කරනවා නම් මේක true කරන්න
keycloak.ssl-required=none # Development වලට none, Production වලට all
keycloak.cors=true # CORS enabled කරනවා නම්

# Server Port
server.port=8081

keycloak.use-resource-role-mappings=true කියන එක හරිම වැදගත්. මොකද මේක true කළොත් තමයි Keycloak client එකට assign කරපු roles, Spring Security වල authorities විදිහට map වෙන්නේ. නැත්නම් realm roles විතරයි map වෙන්නේ.

Spring Security Configuration

Keycloak Spring Boot adapter එකෙන් KeycloakWebSecurityConfigurerAdapter කියන class එක provide කරනවා. මේක extends කරලා අපිට custom security configuration එකක් හදාගන්න පුළුවන්.

import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true) // @PreAuthorize enable කරන්න
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        // optionally set simple authority mapper to convert role names to uppercase
        // and prefix with "ROLE_" for use with @PreAuthorize("hasRole('ROLE_ADMIN')")
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(buildSessionRegistry());
    }

    @Bean
    protected SessionRegistry buildSessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
            .antMatchers("/public/**").permitAll() // Publicly accessible URLs
            .antMatchers("/api/**").authenticated() // /api/ වලින් පටන් ගන්න හැම URL එකටම authenticated user කෙනෙක් ඕනේ
            .anyRequest().permitAll() // Default: Allow all for simplicity, or denyAll()
            .and()
            .csrf().disable(); // REST APIs වලට CSRF අක්‍රීය කරන්න
    }
}

මේ class එකේ @EnableGlobalMethodSecurity(prePostEnabled = true) කියන annotation එක හරිම වැදගත්. මේක තමයි @PreAuthorize වගේ annotation පාවිච්චි කරන්න enable කරන්නේ. configureGlobal method එකේදී අපි SimpleAuthorityMapper() එකක් set කරලා තියෙනවා. මේකෙන් Keycloak වල roles, Spring Security වලට map වෙද්දී, ROLE_ prefix එකක් add කරලා uppercase කරනවා. උදාහරණයක් විදිහට, Keycloak වල තියෙන APP_ADMIN කියන role එක Spring Security වලට ROLE_APP_ADMIN විදිහට map වෙනවා.

Fine-Grained Authorization: @PreAuthorize සමඟ සුරක්ෂිත කිරීම

දැන් තමයි වැඩේට බහින්නේ! අපේ application එකේ methods වලට සහ URLs වලට specific permissions දෙන්නේ කොහොමද කියලා බලමු. Spring Security වල @PreAuthorize කියන annotation එක මේකට හොඳටම ගැලපෙනවා.

Controller Method එකක් සුරක්ෂිත කිරීම

අපි Product API එකක් හදමු.

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @GetMapping
    @PreAuthorize("hasRole('APP_USER') or hasRole('APP_ADMIN') or hasRole('PRODUCT_VIEWER')")
    public String getAllProducts() {
        // මේ method එකට access කරන්න APP_USER, APP_ADMIN, හෝ PRODUCT_VIEWER role එකක් ඕනේ.
        // SimpleAuthorityMapper නිසා මේවා ROLE_APP_USER, ROLE_APP_ADMIN, ROLE_PRODUCT_VIEWER ලෙස map වේ.
        return "සියලුම නිෂ්පාදන මෙහි ඇත!";
    }

    @PostMapping
    @PreAuthorize("hasRole('APP_ADMIN') or hasRole('PRODUCT_EDITOR')")
    public String createProduct() {
        // මේ method එකට access කරන්න APP_ADMIN හෝ PRODUCT_EDITOR role එකක් ඕනේ.
        return "නව නිෂ්පාදනයක් සාදන ලදී!";
    }

    @DeleteMapping("/{id}")
    @PreAuthorize("hasAuthority('ROLE_APP_ADMIN')") // 'ROLE_' prefix එක සමඟ exact authority name එක පාවිච්චි කිරීම.
    public String deleteProduct(@PathVariable String id) {
        // මේ method එකට access කරන්න APP_ADMIN role එක විතරයි ඕනේ.
        return id + " අංක දරණ නිෂ්පාදනය මකා දමන ලදී.";
    }

    @PutMapping("/{id}")
    @PreAuthorize("isAuthenticated() and hasAnyRole('APP_ADMIN', 'PRODUCT_EDITOR')")
    public String updateProduct(@PathVariable String id) {
        // මේ method එකට access කරන්න authenticated user කෙනෙක් වෙන්න ඕනේ වගේම
        // APP_ADMIN හෝ PRODUCT_EDITOR role එකකුත් ඕනේ.
        return id + " අංක දරණ නිෂ්පාදනය update කරන ලදී.";
    }
}

මේ උදාහරණයේදී, අපි Keycloak වල හදපු Realm Roles (APP_ADMIN, APP_USER) සහ Client Roles (PRODUCT_VIEWER, PRODUCT_EDITOR) දෙකම @PreAuthorize එකේ පාවිච්චි කරලා තියෙනවා. SimpleAuthorityMapper() එක නිසා මේවා Spring Security වලට එන්නේ ROLE_APP_ADMIN, ROLE_PRODUCT_VIEWER වගේ විදිහට. ඒ නිසා hasRole('ROLE_APP_ADMIN') වගේ දේවල් ලිව්වොත් වැරදියි. hasRole('APP_ADMIN') හෝ hasAuthority('ROLE_APP_ADMIN') විදිහට ලිවිය යුතුයි. hasRole() කියන එක automatically ROLE_ prefix එක check කරනවා. ඒත් hasAuthority() නම් exact authority string එකම බලාපොරොත්තු වෙනවා.

Expression-Based Security ටිකක් ගැඹුරට

@PreAuthorize එකේදී Spring Expression Language (SpEL) පාවිච්චි කරනවා. මේකෙන් අපිට ගොඩක් complex authorization rules ලියන්න පුළුවන්.

  • hasRole('ROLE_NAME'): User ට දීලා තියෙන roles අතරේ ROLE_NAME කියන role එක තියෙනවද බලනවා.
  • hasAnyRole('ROLE_NAME1', 'ROLE_NAME2'): දීලා තියෙන roles වලින් එකක් හරි තියෙනවද බලනවා.
  • hasAuthority('AUTHORITY_NAME'): User ට දීලා තියෙන authorities අතරේ AUTHORITY_NAME එක තියෙනවද බලනවා. මේකෙන් role එකක් වෙන්නත් පුළුවන්, වෙන custom authority එකක් වෙන්නත් පුළුවන්.
  • isAuthenticated(): User කෙනෙක් authenticated වෙලාද කියලා බලනවා.
  • principal.username == #username: User ගේ username එක path variable එකක තියෙන username එකට සමානද කියලා බලනවා. (e.g., @PreAuthorize("principal.username == #username") on getUser(@PathVariable String username))
  • hasPermission('domainObject', 'permissionName'): මේක custom permission logic වලට.

අපේ Keycloak settings වල keycloak.use-resource-role-mappings=true තියෙන නිසා, Keycloak client roles (e.g., PRODUCT_VIEWER) සහ realm roles (e.g., APP_ADMIN) දෙකම Spring Security වල authorities විදිහට map වෙනවා. ඒ නිසා අපිට hasRole('PRODUCT_VIEWER') හෝ hasRole('APP_ADMIN') වගේ use කරන්න පුළුවන්.

හොඳම පුරුදු සහ ගැටළු විසඳීම

Best Practices:

  • Client Roles භාවිතය: Application-specific authorization වලට හැමවිටම Client Roles පාවිච්චි කරන්න. Realm Roles වඩාත් සුදුසුයි general permissions (e.g., administrator of the Keycloak realm) වලට.
  • Least Privilege Principle: User කෙනෙකුට තමන්ගේ කාර්යය ඉටු කරන්න අවශ්‍ය අවම permissions විතරක් දෙන්න. අනවශ්‍ය permissions දෙන්න එපා.
  • Separate Realms: Production, Staging, Development වගේ environments වලට වෙන වෙනම Keycloak realms පාවිච්චි කරන්න.
  • Consistency: Roles නම් කරනකොට පැහැදිලි, තේරුම්ගත හැකි නම් පාවිච්චි කරන්න. ROLE_ prefix එක spring security වලට map වෙද්දී add වෙන නිසා ඔබේ Keycloak role එකට මේක add කරන්න අවශ්‍ය නැහැ.
  • Logging and Auditing: Authorization failures log කරන්න. ඒ වගේම Security auditing වලට Keycloak වල built-in features පාවිච්චි කරන්න.

Troubleshooting:

  • Role එක හඳුනාගන්නේ නැද්ද?:
    • application.properties එකේ keycloak.use-resource-role-mappings=true ද කියලා බලන්න. Client roles පාවිච්චි කරනවා නම් මේක අනිවාර්යයි.
    • Keycloak වලදී user ට අදාල client එකට role එක assign කරලාද කියලා බලන්න.
    • Spring Security configuration එකේ SimpleAuthorityMapper() එකක් පාවිච්චි කරනවා නම්, role එකේ ROLE_ prefix එක automatic add වෙනවා. ඒ නිසා @PreAuthorize එකේ hasRole('APP_ADMIN') වගේ ලියනවා නම් ඒක වැඩ කරනවා. hasAuthority('ROLE_APP_ADMIN') වගේ ලිව්වොත් exact string එකම ඕනේ.
    • User ගේ JWT token එක decode කරලා (JWT.io වගේ tool එකකින්) roles claims වලට එනවද කියලා බලන්න.
  • Token Validation Issues: Keycloak configuration (auth-server-url, realm, resource) හරියටම ගැලපෙනවද කියලා double check කරන්න.
  • 403 Forbidden Error:
    • User ට අදාල role එක තියෙනවද කියලා බලන්න.
    • @PreAuthorize annotation එකේ typo එකක් තියෙනවද කියලා බලන්න.
    • @EnableGlobalMethodSecurity(prePostEnabled = true) Spring Security config class එකට දාලාද කියලා බලන්න.

අවසන් වශයෙන්

අද අපි කතා කළා Fine-Grained Authorization කියන්නේ මොකක්ද, RBAC වල වැදගත්කම, Keycloak සහ Spring Boot එකට එකතු කරගෙන අපේ applications වලට කොහොමද security layer එකක් add කරන්නේ කියලා. @PreAuthorize වගේ annotations පාවිච්චි කරලා කොහොමද methods සහ URLs වලට specific access rules දෙන්නේ කියලත් අපි බැලුවා.

මේක ඔබේ applications වල security එක ගොඩක් වැඩි කරන වැදගත් පියවරක්. මුලදී පොඩ්ඩක් සංකීර්ණ වගේ පෙනුනත්, මේ concepts හොඳට තේරුම් ගත්තොත් ඔබේ applications ආරක්ෂා කරගන්න එක කිසිම ගැටලුවක් නැතුව කරගන්න පුළුවන්. Keycloak කියන්නේ ගොඩක් බලසම්පන්න tool එකක්, ඒකේ තව ගොඩක් features තියෙනවා Explore කරන්න.

ඉතින්, මේකෙන් ඔබට ප්‍රයෝජනයක් වෙන්න ඇති කියලා හිතනවා. මේ concepts ඔබේ project වලට implement කරලා බලන්න. මොනවා හරි ප්‍රශ්න තියෙනවා නම්, ඔබේ අත්දැකීම් පහත comments වලින් බෙදාගන්න අමතක කරන්න එපා. අපි ඊළඟ ලිපියෙන් හම්බවෙමු!