Skip to main content

JDBC Authentication BCrypt

Materi ini akan membahas cara menerapkan BCrypt, standar enkripsi kata sandi terkuat dan paling banyak digunakan di ekosistem Spring Security pada saat ini.

📌 Apa itu BCrypt?

BCrypt adalah fungsi hashing kata sandi yang dirancang oleh Niels Provos dan David Mazières. Saat ini, BCrypt tetap menjadi pilihan utama karena:

  • Salting Otomatis: Setiap password akan memiliki salt unik (teks acak tambahan), sehingga dua user dengan password sama akan memiliki hasil enkripsi yang berbeda di database. Ini mencegah serangan Rainbow Table.
  • Work Factor (Cost): Kita bisa mengatur seberapa "berat" proses enkripsi dilakukan. Semakin tinggi nilainya, semakin sulit bagi penyerang untuk melakukan brute-force.
tip

Kita bisa menggunakan BCrypt Generator online untuk melakukan Generate dan Verify Hash. Contoh https://bcrypt-generator.com/.

1️⃣ Persiapan Database

  • Spring Security secara default menggunakan struktur tabel tertentu agar dapat bekerja secara otomatis tanpa query tambahan.
CREATE TABLE users (
username VARCHAR(50) NOT NULL PRIMARY KEY,
password VARCHAR(100) NOT NULL,
enabled TINYINT NOT NULL
);

CREATE TABLE authorities (
username VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL,
CONSTRAINT fk_authorities_users FOREIGN KEY(username) REFERENCES users(username)
);
warning
  • Pastikan kolom enabled bernilai 1 agar user bisa login.
  • Selalu pastikan kolom password di database memiliki panjang minimal 68 karakter (disarankan 100) untuk menampung hasil hash BCrypt.
  • Insert data users dan authorities:
INSERT INTO `users` 
VALUES
-- Password = rahasia123 --
('aco','{bcrypt}$2a$12$v8mHo6OSrm/ReDDydgFUneA2n1YIu.j8SFwzFvCjTGuDcTtoewB5W',1),
('ade','{bcrypt}$2a$12$v8mHo6OSrm/ReDDydgFUneA2n1YIu.j8SFwzFvCjTGuDcTtoewB5W',1),
('ucup','{bcrypt}$2a$12$v8mHo6OSrm/ReDDydgFUneA2n1YIu.j8SFwzFvCjTGuDcTtoewB5W',1);

INSERT INTO `authorities`
VALUES
('aco','ROLE_GUEST'),
('ade','ROLE_GUEST'),
('ade','ROLE_USERS'),
('ucup','ROLE_GUEST'),
('ucup','ROLE_USERS'),
('ucup','ROLE_ADMIN');

2️⃣ Dependency

Tambahkan driver database (contoh: MySQL) dan Spring Data JPA ke dalam pom.xml Anda:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>

3️⃣ Konfigurasi Application Properties

Hubungkan aplikasi ke database melalui file application.properties:

spring.datasource.url=jdbc:mysql://localhost:3306/nama_db_anda
spring.datasource.username=root
spring.datasource.password=password_anda
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

4️⃣ Implementasi JDBC Authentication

Pada SecurityConfig, kita menggunakan JdbcUserDetailsManager yang membutuhkan sebuah DataSource:

@Configuration
public class SecurityConfig {

// membuat user konfigurasi dari database
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
return new JdbcUserDetailsManager(dataSource);
}

// konfig custom login page
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {

httpSecurity.authorizeHttpRequests(configurer ->
configurer
.requestMatchers("/").hasRole("GUEST")
.requestMatchers("/user/**").hasAnyRole("USERS", "ADMIN")
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
).formLogin(form ->
form
.loginPage("/loginPage")
.loginProcessingUrl("/authentication")
.permitAll()
).logout(logout ->
logout.permitAll()
).exceptionHandling(configurer ->
configurer.accessDeniedPage("/access-denied")
);

return httpSecurity.build();
}
}
info

TIdak ada perbedaan konfigurasi dengan materi JDBC Authentication sebelumnya, yang menmbedakan hanya isi data pada kolom password tabel users.


🔒 Best Practice: Menggunakan Password Encoder

Pada materi di atas kita masih menggunakan format {bcrypt}$2a$10$dXJ... pada data kolom password tabel users. Kita bisa menggunakan PasswordEncoder untuk menggunakan data hash bcrypt langsung tanpa menggunakan format {bcrypt}$2a$10$dXJ....

1️⃣ Persiapan Database

INSERT INTO `users` 
VALUES
-- Password = rahasia123 --
('aco','$2a$12$v8mHo6OSrm/ReDDydgFUneA2n1YIu.j8SFwzFvCjTGuDcTtoewB5W',1),
('ade','$2a$12$v8mHo6OSrm/ReDDydgFUneA2n1YIu.j8SFwzFvCjTGuDcTtoewB5W',1),
('ucup','$2a$12$v8mHo6OSrm/ReDDydgFUneA2n1YIu.j8SFwzFvCjTGuDcTtoewB5W',1);
  • Contoh Hasil Enkripsi di Database dengan password rahasia123.
+----------+--------------------------------------------------------------+---------+
| username | password | enabled |
+----------+--------------------------------------------------------------+---------+
| aco | $2a$12$v8mHo6OSrm/ReDDydgFUneA2n1YIu.j8SFwzFvCjTGuDcTtoewB5W | 1 |
| ade | $2a$12$v8mHo6OSrm/ReDDydgFUneA2n1YIu.j8SFwzFvCjTGuDcTtoewB5W | 1 |
| ucup | $2a$12$v8mHo6OSrm/ReDDydgFUneA2n1YIu.j8SFwzFvCjTGuDcTtoewB5W | 1 |
+----------+--------------------------------------------------------------+---------+

2️⃣ Konfigurasi Password Encoder

Langkah pertama adalah mendaftarkan BCryptPasswordEncoder sebagai sebuah Bean di dalam kelas SecurityConfig agar bisa digunakan oleh Spring Security secara global. Contoh konfigurasi lengkap:

@Configuration
public class SecurityConfig {

// membuat user konfigurasi dari database
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
return new JdbcUserDetailsManager(dataSource);
}

// Bean untuk Password Encoder BCrypt
@Bean
public PasswordEncoder passwordEncoder() {
// Angka 12 adalah 'High security' (log rounds) cocok production. Standar adalah 10-12.
return new BCryptPasswordEncoder(12);
}

// konfig custom login page
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {

httpSecurity.authorizeHttpRequests(configurer ->
configurer
.requestMatchers("/").hasRole("GUEST")
.requestMatchers("/user/**").hasAnyRole("USERS", "ADMIN")
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
).formLogin(form ->
form
.loginPage("/loginPage")
.loginProcessingUrl("/authentication")
.permitAll()
).logout(logout ->
logout.permitAll()
).exceptionHandling(configurer ->
configurer.accessDeniedPage("/access-denied")
);

return httpSecurity.build();
}
}
tip

Jika aplikasi anda nantinya berkembang sangat besar (jutaan user), cost factor / log rounds 12 mungkin akan terasa sedikit lambat pada hardware server yang pas-pasan (proses hashing bisa memakan waktu >200ms), kita bisa menggunakan cost factor 10 (strength) sebagai alternatif. Namun, untuk standar keamanan, angka 12 adalah pilihan yang sangat solid dan aman dari serangan brute-force.

3️⃣ BONUS: Cara Membuat Password Terenkripsi Secara Manual

Saat mendaftarkan user baru atau memasukkan data awal ke database, kita harus mengenkripsi password tersebut terlebih dahulu. Jangan pernah memasukkan teks biasa ke database.

Contoh Test Code untuk Generate Hash:

public class PasswordGenerator {
public static void main(String[] args) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String rawPassword = "rahasia123";
String encodedPassword = encoder.encode(rawPassword);

System.out.println("Hasil Enkripsi: " + encodedPassword);
}
}