Episode ini membuka Spring Security: filter chain, konfigurasi authentication manager dan user details, form login dan basic auth, serta perlindungan CSRF, CORS, dan security headers untuk aplikasi web dan API yang aman.

Keamanan bukan fitur tambahan — ia lapisan yang melindungi seluruh aplikasi. Episode 12 memperkenalkan Spring Security, standar de facto untuk autentikasi dan otorisasi di ekosistem Spring.
Kalian akan memahami filter chain yang menjadi tulang punggung keamanan, mengonfigurasi user dan authentication manager, memakai form login dan basic auth, serta mengaktifkan perlindungan CSRF, CORS, dan security headers. Episode 13 akan memperluas ini ke JWT dan OAuth 2.0.
Tambahkan starter security ke project:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>Seketika seluruh endpoint terkunci — Spring Security mengaktifkan default yang menolak semua request tanpa autentikasi. Ini adalah perilaku yang aman secara default.
Filter chain adalah rangkaian filter yang memproses setiap request sebelum mencapai controller. Setiap filter memiliki tanggung jawab: membaca credential, mengautentikasi, menerapkan otorisasi, dan menulis header keamanan.
Request -> SecurityContextHolderFilter
-> CsrfFilter
-> UsernamePasswordAuthenticationFilter
-> AuthorizationFilter
-> ControllerMemahami urutan ini membantu membaca stack trace keamanan dan tahu filter mana yang menolak request kalian. Spring Boot 3 memakai SecurityFilterChain berbasis komponen yang dikonfigurasi dengan fluent API.
Autentikasi memerlukan dua komponen: UserDetailsService untuk memuat user, dan PasswordEncoder untuk mencocokkan password. Mulai dengan user dari memory:
@Bean
public UserDetailsService userDetailsService() {
UserDetails admin = User.withUsername("admin")
.password(passwordEncoder().encode("admin123"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(admin);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}Jangan pernah menyimpan password plaintext. BCryptPasswordEncoder menghasilkan hash dengan salt otomatis — standard yang direkomendasikan. Untuk aplikasi nyata, implementasi UserDetailsService memuat user dari database.
Bentuk yang umum di produksi: user disimpan di database dan dimuat lewat repository:
@Service
public class CustomUserDetailsService
implements UserDetailsService {
private final UserRepository repository;
public CustomUserDetailsService(UserRepository repository) {
this.repository = repository;
}
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return repository.findByUsername(username)
.map(user -> User.withUsername(user.getUsername())
.password(user.getPassword())
.roles(user.getRole())
.build())
.orElseThrow(() -> new UsernameNotFoundException(
"User tidak ditemukan"));
}
}Dengan pola ini, autentikasi berasal dari data sungguhan di database. Hash password tetap memakai BCryptPasswordEncoder.
Atur kebijakan keamanan dengan SecurityFilterChain. Contoh memakai form login untuk aplikasi web dan mengizinkan halaman publik:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/home", "/register")
.permitAll()
.requestMatchers("/api/admin/**")
.hasRole("ADMIN")
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.httpBasic(Customizer.withDefaults())
.build();
}Aturan dibaca dari atas ke bawah: halaman publik diizinkan tanpa login, area admin hanya untuk role ADMIN, dan sisanya butuh autentikasi. Urutan requestMatchers sangat menentukan hasil akhir.
CSRF melindungi dari serangan yang memaksa user terautentikasi mengirim request berbahaya. Spring mengaktifkannya secara default dengan token CSRF. Untuk REST API stateless yang memakai token (bukan cookie), CSRF tidak terlalu relevan dan sering dimatikan:
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated())
.build();Untuk aplikasi web berbasis cookie dan form login, biarkan CSRF aktif. Keputusan ini bergantung pada mekanisme autentikasi yang dipakai — token bearer tidak memerlukan CSRF, session cookie memerlukannya.
CORS mengontrol origin mana yang boleh mengakses API dari browser:
return http
.cors(cors -> cors.configurationSource(request -> {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://app.example.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT"));
config.setAllowedHeaders(List.of("Authorization"));
return config;
}))
.build();Jangan buka semua origin kecuali benar-benar diperlukan — batasi ke domain yang kalian kuasai. Selain itu, Spring menambahkan security headers otomatis: X-Content-Type-Options, Strict-Transport-Security, dan frame options, yang melindungi dari berbagai serangan browser.
Uji konfigurasi dengan curl. Tanpa credential, request akan ditolak dengan status 401:
curl -I http://localhost:8080/api/items
curl -u admin:admin123 http://localhost:8080/api/adminPerintah curl -u admin:admin123 http://localhost:8080/api/admin mengirim basic auth — header Authorization yang berisi username dan password. Request pertama tanpa credential akan menerima 401 Unauthorized, dan yang kedua hanya sukses untuk user dengan role ADMIN.
Episode 12 membekali kalian dengan keamanan dasar Spring Security: memahami filter chain, mengonfigurasi authentication manager dengan UserDetailsService dan PasswordEncoder, menetapkan aturan akses di SecurityFilterChain, serta mengaktifkan perlindungan CSRF, CORS, dan security headers.
Inti yang harus dibawa pulang:
spring-boot-starter-security mengunci seluruh endpoint secara default.UserDetailsService memuat user; BCryptPasswordEncoder menghash password.SecurityFilterChain menetapkan aturan otorisasi dengan requestMatchers.Di episode 13 selanjutnya kita akan membahas OAuth 2.0 dan JWT — peran authorization server vs resource server, implementasi token-based auth dengan JWT, integrasi identity provider eksternal seperti Keycloak, serta best practice refresh token dan scopes.