මාතෘකාව 6: ආරක්ෂාව සහ සත්‍යාපනය - ඔබගේ යෙදුම ආරක්ෂා කිරීම

ආරක්ෂාව යනු අමතර විශේෂාංගයක් නොව, අත්තිවාරමකි. අපි Spring Security සමඟින් අපගේ යෙදුම සහ පරිශීලක දත්ත ආරක්ෂා කරමු.

6.1 Spring Security හැඳින්වීම

Spring Security යනු Spring-පාදක යෙදුම් සුරක්ෂිත කිරීම සඳහා වන සම්මත framework එකයි. එය ආරක්ෂාවේ මූලික කුළුණු දෙක හසුරුවන බලවත් සහ ඉහළින් අභිරුචිකරණය කළ හැකි framework එකකි:

  • සත්‍යාපනය (Authentication - AuthN): මෙය "ඔබ කවුද?" යන ප්‍රශ්නයට පිළිතුරු සපයයි. එය පරිශීලකයෙකුගේ අනන්‍යතාවය තහවුරු කිරීමේ ක්‍රියාවලියයි (සාමාන්‍යයෙන් username සහ password පරීක්ෂා කිරීමෙන්).
  • අවසර දීම (Authorization - AuthZ): මෙය "ඔබට කිරීමට අවසර ඇත්තේ කුමක්ද?" යන ප්‍රශ්නයට පිළිතුරු සපයයි. පරිශීලකයෙකු සත්‍යාපනය කළ පසු, නිශ්චිත සම්පතකට ප්‍රවේශ වීමට හෝ ක්‍රියාවක් කිරීමට ඔවුන්ට අවශ්‍ය අවසර තිබේද යන්න තීරණය කරන්නේ authorization මගිනි.

6.2 BCrypt සමඟ මුරපද සංකේතනය (Password Encryption)

ආරක්ෂක නීතිය #1: කිසිවිටෙක මුරපද සරල тексту (Plain Text) ලෙස ගබඩා නොකරන්න

මුරපද එලෙසින්ම (උදා: "password123") ඔබගේ දත්ත සමුදායේ ගබඩා කිරීම ඔබට කළ හැකි විශාලතම ආරක්ෂක වැරැද්දයි. ඔබගේ දත්ත සමුදාය කවදා හෝ අනතුරට ලක් වුවහොත්, සෑම පරිශීලකයෙකුගේම මුරපදය නිරාවරණය වනු ඇත.

විසඳුම වන්නේ එක්-මාර්ග ගුප්ත ලේඛන හැෂිං (one-way cryptographic hashing) ය. අපි මුරපදයක් hash නමින් හැඳින්වෙන දිගු, නියත-දිග තන්තුවක් (string) බවට පරිවර්තනය කිරීමට ඇල්ගොරිතමයක් භාවිතා කරමු. මෙම ක්‍රියාවලිය ආපසු හැරවිය නොහැක; ඔබට hash එකෙන් මුල් මුරපදය නැවත ලබා ගත නොහැක.

BCrypt: කර්මාන්තයේ සම්මතය

අපි මුරපද සඳහා විශේෂයෙන් නිර්මාණය කරන ලද ශක්තිමත් hashing ශ්‍රිතයක් වන BCrypt භාවිතා කරන්නෙමු. එය හිතාමතාම මන්දගාමී කර ඇති අතර, එයට "salt" (hashing කිරීමට පෙර මුරපදය සමඟ මිශ්‍ර කරන ලද අහඹු දත්ත) ඇතුළත් වන නිසා, brute-force වැනි පොදු ප්‍රහාරවලට ඉහළ ප්‍රතිරෝධයක් දක්වයි.

`PasswordEncoder` ක්‍රියාත්මක කිරීම

Spring Security මෙය පහසු කරයි. අපට අවශ්‍ය වන්නේ configuration class එකක PasswordEncoder bean එකක් නිර්වචනය කිරීමෙන් කුමන hashing ඇල්ගොරිතමය භාවිතා කළ යුතුද යන්න පැවසීම පමණි.


@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        // BCrypt hashing ඇල්ගොරිතමය භාවිතා කරන්න
        return new BCryptPasswordEncoder();
    }
    
    // අනෙකුත් ආරක්ෂක වින්‍යාසයන් මෙහි එකතු වනු ඇත
}

6.3 පරිශීලක සත්‍යාපනය (Login System) එක් කිරීම

පරිශීලකයින් සත්‍යාපනය කිරීමට, Spring Security හට ඔවුන්ගේ විස්තර සොයා ගැනීමට ක්‍රමයක් අවශ්‍ය වේ. අපි UserDetailsService interface එක ක්‍රියාත්මක කිරීමෙන් මෙය සපයන්නෙමු.

පියවර 1: User Entity එක

පළමුව, අපගේ දත්ත සමුදායේ credentials ගබඩා කිරීමට අපට `User` entity එකක් අවශ්‍ය වේ.


@Entity
@Table(name = "users")
public class User implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String username;
    private String password; // මෙය BCrypt hash එකක් ලෙස ගබඩා වනු ඇත
    private String role; // උදා: "ROLE_USER", "ROLE_ADMIN"
    
    // UserDetails interface හි methods මෙහි ක්‍රියාත්මක කළ යුතුය
}

පියවර 2: `UserDetailsService` ක්‍රියාත්මක කිරීම

මෙම service එකේ එකම කාර්යය වන්නේ username එක මගින් දත්ත සමුදායෙන් පරිශීලකයෙකු load කිරීමයි.


@Service
public class JpaUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("පරිශීලකයා හමු නොවීය: " + username));
    }
}

පියවර 3: ලියාපදිංචි කිරීමේ Logic එක

අපගේ `UserService` හි, නව පරිශීලකයෙකු සාදන විට, අපි මුරපදය encode කළ යුතුය.


@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;

    public User registerUser(User user) {
        // සුරැකීමට පෙර සරල මුරපදය encode කරන්න
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        user.setRole("ROLE_USER");
        return userRepository.save(user);
    }
}

6.4 භූමිකාව මත පදනම් වූ ප්‍රවේශය: Admin සහ Customer

Authorization යනු ප්‍රවේශය පාලනය කිරීමයි. සාමාන්‍ය පාරිභෝගිකයෙකුට වෙනත් පාරිභෝගිකයෙකුගේ ගිණුම් විස්තර බැලීමට නොහැකි විය යුතු අතර, admin කෙනෙකුට පුළුල් අවසර අවශ්‍ය වේ. අපි මෙම නීති අපගේ `SecurityConfig` class එකේ SecurityFilterChain bean එකක් නිර්වචනය කිරීමෙන් configure කරන්නෙමු.


// --- SecurityConfig.java තුළ ---
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .csrf(csrf -> csrf.disable()) // API testing සරල කිරීමට CSRF අක්‍රීය කරන්න
        .authorizeHttpRequests(auth -> auth
            // ලියාපදිංචි වීමේ සහ login endpoints වෙත ඕනෑම කෙනෙකුට පිවිසීමට ඉඩ දෙන්න
            .requestMatchers("/api/auth/**").permitAll()
            
            // /api/admin යටතේ ඇති ඕනෑම endpoint එකක් සඳහා ADMIN භූමිකාව අවශ්‍ය වේ
            .requestMatchers("/api/admin/**").hasRole("ADMIN")
            
            // ගිණුම් සම්බන්ධ endpoints සඳහා USER හෝ ADMIN භූමිකාව අවශ්‍ය වේ
            .requestMatchers("/api/accounts/**").hasAnyRole("USER", "ADMIN")
            
            // අනෙකුත් ඕනෑම ඉල්ලීමක් සත්‍යාපනය කළ යුතුය
            .anyRequest().authenticated()
        )
        .httpBasic(Customizer.withDefaults());

    return http.build();
}