API yang hanya mengandalkan token statis rentan disalahgunakan saat token bocor atau dicuri. Artikel ini langsung menjawab bagaimana Spring Boot dapat menguatkan otentikasi API dengan kombinasi OAuth2 Resource Server, rotasi token, sesi stateless, dan penyimpanan secret di vault atau keystore. Konfigurasi yang dijelaskan siap langsung diadaptasi untuk layanan produksi.

Menetapkan dasar keamanan API dengan Spring Security & OAuth2 Resource Server

Mulai dengan menjadikan Spring Boot API sebagai OAuth2 Resource Server. Resource server hanya menerima request yang membawa token JWT atau opaque yang sudah diverifikasi. Di Spring Security, kita cukup mengaktifkan oauth2ResourceServer dan mengatur jwt atau opaqueToken validation.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(HttpMethod.GET, "/public/**").permitAll()
                .anyRequest().authenticated())
            .oauth2ResourceServer(oauth -> oauth
                .jwt(jwt -> jwt
                    .jwtAuthenticationConverter(new MyJwtConverter())));
        return http.build();
    }
}

Dengan sesi stateless, tidak ada cache server-side. Token wajib diverifikasi tiap request. MyJwtConverter bisa mengonversi klaim menjadi GrantedAuthority sesuai kebutuhan API Anda.

Implementasi token rotasi untuk mengurangi dampak token yang bocor

Token rotasi cocok untuk refresh token yang panjang umurnya. Saat klien meminta refresh, sistem menerbitkan refresh token baru dan menonaktifkan token lama. Ini meminimalkan ruang gerak penyerang jika swipe token terjadi.

Alur rotasi sederhana:

1. Klien mengirim refresh token ke endpoint /auth/refresh
2. Server mengecek apakah token masih aktif & sesuai fingerprint (misal IP, user-agent hashed)
3. Jika valid, server menandai token lama sebagai kadaluarsa dan membuat refresh token baru
4. Akses token baru diterbitkan sekaligus refresh token baru dikirim respon
5. Token lama disimpan dalam blacklist sementara untuk mencegah replay

Gunakan tabel sederhana untuk menyimpan metadata refresh token:

CREATE TABLE refresh_tokens (
    token_id UUID PRIMARY KEY,
    user_id UUID NOT NULL,
    issued_at TIMESTAMP WITH TIME ZONE NOT NULL,
    expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
    fingerprint TEXT NOT NULL,
    revoked BOOLEAN DEFAULT FALSE
);

Pastikan fingerprint menggabungkan informasi request yang sulit dipalsukan (misal HMAC IP+user-agent). Setiap rotasi, ubah status token lama menjadi revoked. Ingat, rotasi harus cepat agar klien tidak kebingungan menunggu token baru.

Mengatur Vault atau keystore untuk menyimpan secret secara aman

Jangan letakkan secret langsung di application.yml. Ambil client secrets, keystore, atau signing key dari vault seperti HashiCorp Vault atau AWS Secrets Manager.

Contoh integrasi Spring dengan Vault:

spring:
  cloud:
    vault:
      uri: https://vault.example.com
      token: ${VAULT_TOKEN}
      kv:
        enabled: true
        backend: kv-v2
      authentication: TOKEN

Dengan konfigurasi di atas, property seperti spring.security.oauth2.resourceserver.jwt.jwk-set-uri bisa diisi langsung dari secret engine Vault. Pastikan aplikasi hanya memiliki akses read ke path tertentu, lalu audit log vault digunakan untuk deteksi akses mencurigakan.

Jika menggunakan keystore lokal, baca private key lewat KeyStore Java sebelum dipakai menandatangani JWT. Gunakan KeyStore.getInstance("PKCS12") dan jangan simpan password di sumber kode.

Perkuat input, upload, dan rate limiting untuk mencegah abuse

API yang menerima input dari pengguna harus menutup celah injection. Terapkan validasi eksplisit, misalnya menggunakan @Validated, dan hindari memproses data yang tidak divalidasi.

  • Validasi input: Gunakan DTO dengan anotasi @Size, @Pattern, dan @NotBlank. Gunakan BindingResult untuk menolak request yang tidak valid sebelum business logic dijalankan.
  • Upload aman: Batasi jenis file dengan MIME type, ukuran maksimum, dan simpan file di storage yang terisolasi. Jangan langsung mengeksekusi file; pemeriksaan antivirus/cache bisa diterapkan.
  • Rate limiting: Pakai Bucket4j atau Redis-based token bucket untuk menghitung request per IP/klien. Terapkan level global dan per-endpoint.

Contoh konfigurasi Bucket4j:

Bucket bucket = Bucket4j.builder()
    .addLimit(Bandwidth.simple(60, Duration.ofMinutes(1)))
    .build();

if (bucket.tryConsume(1)) {
    // proses request
} else {
    response.setStatus(429);
}

Gabungkan rate limiting dengan cache ID klien agar tidak bereaksi berlebih terhadap hotspot API tertentu.

Monitoring dan alert untuk sesi stateless & secret

Tanpa sesi, monitoring bergantung pada metrik token. Lacak jumlah token refresh yang ditolak, jumlah token baru yang diterbitkan, dan latensi response server OAuth.

  • Alert sesi/secret: Pantau vault access failure, rotasi token yang terus-menerus gagal, atau key compromise sinyal. Koneksi vault yang terputus harus membunyikan alarm karena menghalangi pembaruan secret.
  • Audit: Catat informasi sesi seperti ID klien, fingerprint, dan aksi rotasi refresh. Gunakan ELK/Prometheus untuk visualisasi.
  • Tracing: Hubungkan trace request dengan token untuk mempermudah penyelidikan insiden.

Jika vault tidak tersedia, sistem harus gagal dengan gracefully (misal fallback ke read-only cache). Jangan biarkan API terus berjalan dengan secret kadaluarsa.

Kapan harus memilih pendekatan tertentu?

Jika API Anda menerbitkan JWT tanpa rotasi, tambahkan rotasi jika aplikasi memiliki sesi lama atau akses offline. Gunakan vault jika terdapat banyak secret atau tim operasi memerlukan audit. Rate limiting wajib untuk API publik dengan trafik tinggi.

Implementasi yang dijelaskan mendorong OAuth2 Resource Server, rotasi token yang aman, penyimpanan secret yang terkunci, plus proteksi input dan monitoring aktif. Dengan begitu, API Spring Boot menjadi lebih tahan terhadap penyalahgunaan dan kebocoran.