Custom Login Page
Materi ini akan membahas cara mengganti halaman login default Spring Security buatan sendiri menggunakan Thymeleaf.
Step by step membuat custom login Spring Security:
- Update SecurityConfig.
- Membuat Controller.
- Membuat View untuk custom Login Page.
1️⃣ Update Security Config
Kita harus memberi tahu Spring Security bahwa kita akan menggunakan URL sendiri untuk halaman login.
@Configuration
public class SecurityConfig {
// membuat user yang hanya running di memory
@Bean
public InMemoryUserDetailsManager userDetailsManager() {
UserDetails aco = User.builder()
.username("aco")
.password("{noop}test123")
.roles("GUEST")
.build();
UserDetails ade = User.builder()
.username("ade")
.password("{noop}test123")
.roles("GUEST", "USERS")
.build();
UserDetails ucup = User.builder()
.username("ucup")
.password("{noop}test123")
.roles("GUEST", "USERS", "ADMIN")
.build();
return new InMemoryUserDetailsManager(aco, ade, ucup);
}
// konfigurasi custom login page
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeHttpRequests(configurer ->
configurer
.anyRequest().authenticated() // Semua request wajib login
).formLogin(form ->
form
.loginPage("/loginPage") // URL yang akan dipanggil (Controller)
.loginProcessingUrl("/authentication") // URL POST yang ditangani Spring Security
.permitAll() // Izinkan semua orang mengakses halaman login
);
return httpSecurity.build();
}
}
loginPage: URL yang akan kita buat di Controller.loginProcessingUrl: Kita tidak perlu membuat Controller untuk ini. Spring Security secara otomatis akan memproses data POST dari form ke URL ini.
2️⃣ Membuat Controller
Buatlah controller sederhana untuk menampilkan file HTML login kita.
@Controller
public class LoginController {
@GetMapping("/loginPage")
public String login() {
return "login-page";
}
}
3️⃣ Membuat View untuk custom Login Page
Gunakan Thymeleaf untuk membuat form. Spring Security secara otomatis mencari parameter username dan password. Buat file login-page.html di src/main/resources/templates/:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login Page</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.error {
color: orangered;
}
</style>
</head>
<body>
<h2>Login</h2>
<form th:action="@{/authentication}" method="post">
<!-- Check login error -->
<div th:if="${param.error}">
<i class="error">Upps!!! your username / password invalid</i>
</div>
<p>Username: <input type="text" name="username" /></p>
<p>Password: <input type="password" name="password" /></p>
<input type="submit" value="Login" />
</form>
</body>
</html>
📌 Poin Penting dalam Form Login
method="POST": Wajib menggunakan POST.name="username"&name="password": Spring Security mencari nama input ini secara default.- CSRF Token: Jika Anda menggunakan Thymeleaf
th:action, Spring Boot akan otomatis menyisipkan token CSRF tersembunyi untuk keamanan. Ini adalah fitur wajib untuk mencegah serangan Cross-Site Request Forgery. - Handling Error: Spring Security akan mengarahkan kembali ke
/loginPage?errorjika login gagal. Kita menangkapnya denganth:if="${param.error}".
- Halaman Login.
- Halaman Login jika autentikasi gagal.
info
✨ Menggunakan Bootstrap
Untuk mempercantik tampilan, kita bisa mengintegrasikan framework CSS seperti Bootstrap melalui CDN di dalam file HTML tersebut.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login Page</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
<style>
body {
background-color: #f8f9fa;
}
.login-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.card-login {
width: 100%;
max-width: 400px;
padding: 20px;
border-radius: 15px;
border: none;
}
</style>
</head>
<body>
<div class="container login-container">
<div class="card card-login shadow">
<div class="card-body">
<h3 class="text-center mb-4 fw-bold">Selamat Datang</h3>
<p class="text-muted text-center mb-4">Silakan masuk ke akun Anda</p>
<form th:action="@{/authentication}" method="post">
<!-- Pesan Error -->
<div th:if="${param.error}">
<div class="alert alert-danger py-2" role="alert">
<small>Upps!!! username atau kata sandi Anda salah.</small>
</div>
</div>
<!-- Input Username -->
<div class="form-floating mb-3">
<input type="text" class="form-control" id="floatingInput" name="username" placeholder="Username"
required>
<label for="floatingInput">Username</label>
</div>
<!-- Input Password -->
<div class="form-floating mb-3">
<input type="password" class="form-control" id="floatingPassword" name="password" placeholder="Password"
required>
<label for="floatingPassword">Kata Sandi</label>
</div>
<!-- Tombol Login -->
<button class="btn btn-primary w-100 py-2 fw-bold" type="submit">Masuk</button>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
crossorigin="anonymous"></script>
</body>
</html>
- Halaman Login.

- Halaman Login jika autentikasi gagal.
