Spring Security එකෙන් Role-Based Security හදමු - Complete Guide

Spring Security එකෙන් Role-Based Security හදමු - Complete Guide

ආයුබෝවන් කට්ටිය! කොහොමද ඉතින්? අද අපි කතා කරන්න යන්නේ ඔයාලා හැමෝටම ගොඩක් වැදගත් වෙන, ඕනෑම Application එකක අනිවාර්යයෙන්ම තියෙන්න ඕන දෙයක් ගැන – ඒ තමයි Role-Based Security. විශේෂයෙන්ම Spring Security framework එක භාවිතා කරලා අපේ Spring Boot Applications වලට මේ ආරක්ෂාව කොහොමද එකතු කරගන්නේ කියලා අපි පියවරෙන් පියවර බලමු.

ඔයාලා Backend Development කරනවා නම්, API ලියනවා නම් මේ ගැන අනිවාර්යයෙන්ම දැනගෙන ඉන්න එක අත්‍යාවශ්‍යයි. මොකද, හැමෝටම හැම දේම කරන්න දෙන්න බැහැනේ, නේද? සමහරුන්ට දත්ත බලන්න පුළුවන් වෙන්න ඕනේ, සමහරුන්ට ඒවා වෙනස් කරන්න පුළුවන් වෙන්න ඕනේ, තව සමහරුන්ට අලුත් දේවල් එකතු කරන්න පුළුවන් වෙන්න ඕනේ. මේක හරියට බැංකුවක වගේ; හැමෝටම බැංකුවේ හැම කාමරේටම යන්න දෙන්නේ නැහැනේ. ඒ වගේ තමයි අපේ Software වලත්.

ඇයි අපිට මේ Role-Based Security ඕනේ?

හිතලා බලන්නකෝ, ඔයා Software එකක් හදනවා, ඒක පාවිච්චි කරන්නේ ගොඩක් අය. ඒ අය අතරින් සමහරු Administrators (ADMIN), සමහරු සාමාන්‍ය පරිශීලකයෝ (USER), තව සමහරු ආයතනයක ප්‍රධානීන් (MANAGER) වෙන්න පුළුවන්. දැන් මේ හැමෝටම එකම වරප්‍රසාද (privileges) දෙන්න බැහැනේ. උදාහරණයක් විදිහට:

  • ADMIN කෙනෙකුට System එකේ හැම දේම කරන්න පුළුවන් වෙන්න ඕනේ – අලුත් user කෙනෙක් add කරන්න, පරණ user කෙනෙක් delete කරන්න, settings වෙනස් කරන්න.
  • USER කෙනෙකුට පුළුවන් වෙන්න ඕනේ තමන්ගේ profile එක බලන්න, තමන්ගේ order history එක බලන්න වගේ දේවල් විතරයි. System එකේ අනිත් user ලාගේ දත්ත බලන්න හරි delete කරන්න හරි බැරි වෙන්න ඕනේ.

මෙන්න මේ වගේ අවස්ථාවලදී අපේ Application එක ආරක්ෂා කරගන්න Role-Based Security අත්‍යවශ්‍යයි. මේකෙන් වෙන්නේ, යම්කිසි user කෙනෙක්ට Application එකේ මොන කොටස් වලට යන්න පුලුවන්ද, මොනවද කරන්න පුළුවන් කියන එක අපි කලින්ම තීරණය කරලා, ඒ අනුව access එක පාලනය කරන එක. මේක Authorization කියලා තමයි හඳුන්වන්නේ.

Spring Security වල Role-Based Security වැඩ කරන හැටි

Spring Security කියන්නේ Java Applications වල Security හදන්න තියෙන සුපිරිම framework එකක්. මේකේදී Role-Based Security ක්‍රියාත්මක වෙන්නේ මූලික සංකල්ප දෙකක් මතයි:

  1. Authentication (සත්‍යාපනය): User කවුද කියලා හඳුනාගන්න එක. එයාගේ username, password හරිද කියලා බලන එක.
  2. Authorization (අවසර දීම): User කවුද කියලා දැනගත්තට පස්සේ, එයාට මොනවද කරන්න පුළුවන් කියලා තීරණය කරන එක. මෙතනදී තමයි Roles (ADMIN, USER) වගේ දේවල් වැදගත් වෙන්නේ.

Spring Security වලදී අපිට Roles එකතු කරන්න විවිධ ක්‍රම තියෙනවා. අපි අද බලමු සාමාන්‍යයෙන් භාවිතා වෙන, Spring Boot project එකකට පහසුවෙන් එකතු කරගන්න පුළුවන් ක්‍රමයක්.

ප්‍රායෝගිකව Spring Boot එකට Role-Based Security එකතු කරමු

අපි මේක පියවරෙන් පියවර කරමු. ඔයාලට අලුත් Spring Boot project එකක් හදාගන්න පුළුවන් Spring Initializr එකෙන්.

1. අවශ්‍ය Dependencies එකතු කරගමු

ඔයාගේ pom.xml (Maven) file එකට පහත Dependencies එකතු කරන්න:

<dependencies>
    <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.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

spring-boot-starter-security එක තමයි අපිට Security Features සේරම ගෙනත් දෙන්නේ. spring-boot-starter-web එක API endpoints හදන්න.

2. Security Configuration Class එක හදමු

දැන් අපි Security Configuration file එක හදමු. මේකට අපි WebSecurityConfigurerAdapter (Spring Boot 2.x වලට) භාවිතා කරනවා. අලුත් Spring Boot 3.x වලට නම් SecurityFilterChain bean එකක් විදිහට තමයි configure කරන්නේ. මේ උදාහරණය 2.x වලට වැඩිපුර ගැලපෙනවා, හැබැයි concepts එකමයි.

SecurityConfig.java කියලා class එකක් හදලා මේ code එක දාන්න:

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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable() // For simplicity, disable CSRF for now. Be careful in production!
            .authorizeRequests()
                .antMatchers("/public").permitAll() // Anyone can access this endpoint
                .antMatchers("/user/**").hasAnyRole("USER", "ADMIN") // Only USER or ADMIN roles
                .antMatchers("/admin/**").hasRole("ADMIN") // Only ADMIN role
                .anyRequest().authenticated() // All other requests need authentication
            .and()
            .httpBasic(); // Use basic authentication
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        // In-memory user store for demonstration purposes
        // In a real application, you'd use a database (e.g., JPA, JDBC)
        UserDetails user = User.builder()
                .username("user")
                .password(passwordEncoder().encode("password"))
                .roles("USER")
                .build();

        UserDetails admin = User.builder()
                .username("admin")
                .password(passwordEncoder().encode("adminpass"))
                .roles("ADMIN", "USER") // Admin also has USER roles
                .build();

        return new InMemoryUserDetailsManager(user, admin);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

මේ Code එකේ මොනවද වෙන්නේ?

  • @Configuration සහ @EnableWebSecurity: මේකෙන් Spring Security Configuration එකක් කියලා Spring Framework එකට කියනවා.
  • configure(HttpSecurity http) method එක: මේක තමයි ප්‍රධානම තැන. මෙතනදී අපි අපේ URL patterns වලට Role-Based access control දානවා.
    • .antMatchers("/public").permitAll(): /public කියන URL එකට ඕනෑම කෙනෙකුට authenticate නොවී access කරන්න පුළුවන්.
    • .antMatchers("/user/**").hasAnyRole("USER", "ADMIN"): /user/ වලින් පටන් ගන්න හැම URL එකකටම USER හෝ ADMIN role එක තියෙන අයට විතරයි access කරන්න පුළුවන්.
    • .antMatchers("/admin/**").hasRole("ADMIN"): /admin/ වලින් පටන් ගන්න හැම URL එකකටම ADMIN role එක තියෙන අයට විතරයි access කරන්න පුළුවන්.
    • .anyRequest().authenticated(): මේකෙන් කියන්නේ මේ ටිකට අමතරව අනිත් හැම request එකකටම authenticate වෙලාම තමයි access කරන්න පුළුවන් වෙන්නේ කියලා. (role එකක් නැතුව වුණත්).
    • .httpBasic(): අපි මේ උදාහරණයේදී Basic Authentication භාවිතා කරනවා. ඒ කියන්නේ username, password request header එකේ යවන එක.
  • userDetailsService() method එක: මේකෙන් අපි Application එකට user ලා කවුද, එයාලගේ passwords මොනවද, එයාලට තියෙන roles මොනවද කියලා කියනවා. මේ උදාහරණයේදී අපි සරලව in-memory users (user:password with USER role, admin:adminpass with ADMIN, USER roles) දෙන්නෙක් හැදුවා. Real world applications වලදී මේ user details database එකකින් load කරනවා.
  • passwordEncoder() method එක: Passwords encrypt කරන්න (hash කරන්න) අපි BCryptPasswordEncoder භාවිතා කරනවා. මේක ඉතාමත් වැදගත් Security practice එකක්. කවදාවත් passwords plain text විදිහට store කරන්න එපා!

3. Controller Endpoints හදමු

දැන් අපි Test කරන්න Endpoint කීපයක් හදමු. TestController.java කියලා class එකක් හදලා මේ code එක දාන්න:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/public")
    public String publicEndpoint() {
        return "This is a public endpoint. Anyone can access this!";
    }

    @GetMapping("/user/dashboard")
    public String userDashboard() {
        return "Welcome to the User Dashboard! You have USER or ADMIN role.";
    }

    @GetMapping("/admin/settings")
    public String adminSettings() {
        return "Welcome to the Admin Settings! Only ADMINs can see this.";
    }

    @GetMapping("/any/authenticated/endpoint")
    public String anyAuthenticatedEndpoint() {
        return "You are authenticated, but no specific role required for this.";
    }
}

පරීක්ෂා කරලා බලමු! (Let's Test It!)

දැන් ඔයාට පුළුවන් මේ Project එක Run කරලා Postman, curl, නැත්නම් web browser එකක් පාවිච්චි කරලා Test කරන්න. Spring Boot Application එක Run කලාම මේක සාමාන්‍යයෙන් http://localhost:8080 එකෙන් listen කරනවා.

Any Authenticated Endpoint (No specific role):

curl -u user:password http://localhost:8080/any/authenticated/endpoint

Output: You are authenticated, but no specific role required for this. (Authentication වෙලා තියෙන නිසා access ලැබෙනවා)

curl http://localhost:8080/any/authenticated/endpoint

Output: HTTP/1.1 401 Unauthorized (Authenticate නොවී access කරන්න බැහැ)

Admin Endpoint (USER role - Unauthorized):

curl -u user:password http://localhost:8080/admin/settings

Output: HTTP/1.1 403 Forbidden (USER role එකට ADMIN endpoints වලට access නැති නිසා Forbidden message එක එනවා)

Admin Endpoint (ADMIN role):

curl -u admin:adminpass http://localhost:8080/admin/settings

Output: Welcome to the Admin Settings! Only ADMINs can see this. (ADMIN role එක තියෙන නිසා access ලැබෙනවා)

User Endpoint (ADMIN role):

curl -u admin:adminpass http://localhost:8080/user/dashboard

Output: Welcome to the User Dashboard! You have USER or ADMIN role. (ADMIN ට USER role එකත් තියෙන නිසා access ලැබෙනවා)

User Endpoint (USER role):

curl -u user:password http://localhost:8080/user/dashboard

Output: Welcome to the User Dashboard! You have USER or ADMIN role. (USER role එක තියෙන නිසා access ලැබෙනවා)

Public Endpoint:

curl http://localhost:8080/public

Output: This is a public endpoint. Anyone can access this! (Authentication අවශ්‍ය නැහැ)

සාරාංශය සහ ඉදිරි පියවර

දැන් ඔයාලට පැහැදිලියි නේද Spring Security භාවිතා කරලා Role-Based Security කොහොමද අපේ Applications වලට එකතු කරන්නේ කියලා. මේක ඕනෑම Application එකක අත්‍යාවශ්‍ය Security Layer එකක්. අපි මේ උදාහරණයේදී සරල in-memory users භාවිතා කළත්, Real applications වලදී User Details Database එකකින් load කරන විදිහ සහ custom UserDetailsService එකක් හදන විදිහ ඉගෙන ගන්න එක ගොඩක් වැදගත්.

අමතරව, @PreAuthorize, @PostAuthorize වගේ Annotation භාවිතා කරලා method level Security හදන විදිහ ගැනත් ඉදිරියේදී කතා කරන්න පුළුවන්. ඒ වගේම JWT (JSON Web Tokens) වගේ stateless authentication mechanism භාවිතා කරන හැටිත් බලමු.

ඔයාලා මේ code එක try කරලා බැලුවා නම්, වැඩේ ගොඩද කියලා comment එකක් දාලා යන්න! මොනවා හරි ප්‍රශ්න තියෙනවා නම් අහන්න. අපි ඊළඟ ලිපියෙන් හම්බවෙමු! ජය වේවා!