validasi login
This commit is contained in:
parent
a97343dd39
commit
4d686d031e
|
|
@ -12,41 +12,85 @@
|
||||||
|
|
||||||
class LoginRequest extends FormRequest
|
class LoginRequest extends FormRequest
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Izinkan semua user akses request ini
|
||||||
|
*/
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aturan validasi input
|
||||||
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'username' => ['required', 'string'],
|
'username' => ['required', 'string'],
|
||||||
'password' => ['required', 'string'],
|
'password' => ['required', 'string', 'min:8'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authenticate(): void
|
/**
|
||||||
{
|
* Pesan error custom (biar lebih user-friendly)
|
||||||
|
*/
|
||||||
|
public function messages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'username.required' => 'Username wajib diisi.',
|
||||||
|
'username.string' => 'Username harus berupa teks.',
|
||||||
|
|
||||||
|
'password.required' => 'Password wajib diisi.',
|
||||||
|
'password.string' => 'Password harus berupa teks.',
|
||||||
|
'password.min' => 'Password minimal 6 karakter.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proses autentikasi login
|
||||||
|
*/
|
||||||
|
public function authenticate(): void
|
||||||
|
{
|
||||||
|
// Cek limit login (anti brute force)
|
||||||
|
$this->ensureIsNotRateLimited();
|
||||||
|
|
||||||
|
// Jika dua-duanya kosong (optional tambahan biar lebih jelas)
|
||||||
|
if (!$this->username && !$this->password) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'username' => 'Username dan password wajib diisi.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cek apakah username ada
|
||||||
$user = User::where('username', $this->username)->first();
|
$user = User::where('username', $this->username)->first();
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
'username' => 'Username tidak terdaftar.',
|
'username' => 'Username tidak terdaftar.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cek password
|
||||||
if (!Auth::attempt([
|
if (!Auth::attempt([
|
||||||
'username' => $this->username,
|
'username' => $this->username,
|
||||||
'password' => $this->password,
|
'password' => $this->password,
|
||||||
])) {
|
])) {
|
||||||
|
RateLimiter::hit($this->throttleKey());
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
'password' => 'Password yang dimasukkan salah.',
|
'password' => 'Password yang dimasukkan salah.',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Reset limit kalau berhasil login
|
||||||
|
RateLimiter::clear($this->throttleKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cek apakah sudah terlalu banyak percobaan login
|
||||||
|
*/
|
||||||
public function ensureIsNotRateLimited(): void
|
public function ensureIsNotRateLimited(): void
|
||||||
{
|
{
|
||||||
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
if (!RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,16 +99,17 @@ public function ensureIsNotRateLimited(): void
|
||||||
$seconds = RateLimiter::availableIn($this->throttleKey());
|
$seconds = RateLimiter::availableIn($this->throttleKey());
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
'username' => trans('auth.throttle', [
|
'username' => 'Terlalu banyak percobaan login. Coba lagi dalam ' . $seconds . ' detik.',
|
||||||
'seconds' => $seconds,
|
|
||||||
'minutes' => ceil($seconds / 60),
|
|
||||||
]),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key unik untuk rate limiter
|
||||||
|
*/
|
||||||
public function throttleKey(): string
|
public function throttleKey(): string
|
||||||
{
|
{
|
||||||
return Str::transliterate(Str::lower($this->string('username')) . '|' . $this->ip());
|
return Str::transliterate(
|
||||||
|
Str::lower($this->input('username')) . '|' . $this->ip()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,15 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<!-- Required meta tags -->
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<title>Login Admin | SIG TPS Kabupaten Nganjuk</title>
|
<title>Login Admin | SIG TPS Kabupaten Nganjuk</title>
|
||||||
<!-- plugins:css -->
|
|
||||||
|
<!-- CSS -->
|
||||||
<link rel="stylesheet" href="{{ asset('assets/admin/vendors/feather/feather.css') }}">
|
<link rel="stylesheet" href="{{ asset('assets/admin/vendors/feather/feather.css') }}">
|
||||||
<link rel="stylesheet" href="{{ asset('assets/admin/vendors/ti-icons/css/themify-icons.css') }}">
|
<link rel="stylesheet" href="{{ asset('assets/admin/vendors/ti-icons/css/themify-icons.css') }}">
|
||||||
<link rel="stylesheet" href="{{ asset('assets/admin/vendors/css/vendor.bundle.base.css') }}">
|
<link rel="stylesheet" href="{{ asset('assets/admin/vendors/css/vendor.bundle.base.css') }}">
|
||||||
<!-- endinject -->
|
|
||||||
<!-- Plugin css for this page -->
|
|
||||||
<!-- End plugin css for this page -->
|
|
||||||
<!-- inject:css -->
|
|
||||||
<link rel="stylesheet" href="{{ asset('assets/admin/css/vertical-layout-light/style.css') }}">
|
<link rel="stylesheet" href="{{ asset('assets/admin/css/vertical-layout-light/style.css') }}">
|
||||||
<!-- endinject -->
|
|
||||||
<link rel="shortcut icon" href="{{ asset('assets/admin/images/iconsig.png') }}" />
|
<link rel="shortcut icon" href="{{ asset('assets/admin/images/iconsig.png') }}" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
@ -25,63 +20,79 @@
|
||||||
<div class="px-0 content-wrapper d-flex align-items-center auth">
|
<div class="px-0 content-wrapper d-flex align-items-center auth">
|
||||||
<div class="mx-0 row w-100">
|
<div class="mx-0 row w-100">
|
||||||
<div class="mx-auto col-lg-4">
|
<div class="mx-auto col-lg-4">
|
||||||
|
|
||||||
<div class="px-4 py-5 text-left auth-form-light px-sm-5">
|
<div class="px-4 py-5 text-left auth-form-light px-sm-5">
|
||||||
<div class="brand-logo">
|
<div class="text-center brand-logo">
|
||||||
<img src="{{ asset('assets/admin/images/sig-logo.png') }}" alt="logo">
|
<img src="{{ asset('assets/admin/images/sig-logo.png') }}" alt="logo">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4>Halo Admin!</h4>
|
<h4>Halo Admin!</h4>
|
||||||
<h6 class="font-weight-light">Silakan masuk untuk melanjutkan.</h6>
|
<h6 class="font-weight-light">Silakan masuk untuk melanjutkan.</h6>
|
||||||
<form class="pt-3" method="POST" action="{{ route('login') }}">
|
|
||||||
|
<!-- FORM LOGIN -->
|
||||||
|
<form class="pt-3" method="POST" action="{{ route('login') }}" novalidate>
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
@if ($errors->any())
|
<!-- ERROR GLOBAL -->
|
||||||
<div class="mb-4 alert alert-danger">
|
{{-- @if ($errors->any())
|
||||||
|
<div class="alert alert-danger">
|
||||||
{{ $errors->first() }}
|
{{ $errors->first() }}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif --}}
|
||||||
|
|
||||||
|
<!-- USERNAME -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text"
|
<input type="text" name="username"
|
||||||
class="form-control form-control-lg {{ session('error') ? 'is-invalid' : '' }}"
|
class="form-control form-control-lg @error('username') is-invalid @enderror"
|
||||||
name="username" placeholder="Username" value="{{ old('username') }}" required>
|
placeholder="Username" value="{{ old('username') }}">
|
||||||
|
|
||||||
|
@error('username')
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
{{ $message }}
|
||||||
|
</div>
|
||||||
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- PASSWORD -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="password"
|
<input type="password" name="password"
|
||||||
class="form-control form-control-lg {{ session('error') ? 'is-invalid' : '' }}"
|
class="form-control form-control-lg @error('password') is-invalid @enderror"
|
||||||
name="password" placeholder="Password" required>
|
placeholder="Password">
|
||||||
|
|
||||||
|
@error('password')
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
{{ $message }}
|
||||||
|
</div>
|
||||||
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- BUTTON -->
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
class="btn btn-block btn-primary btn-lg font-weight-medium auth-form-btn">
|
class="btn btn-block btn-primary btn-lg font-weight-medium auth-form-btn">
|
||||||
MASUK
|
MASUK
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
<!-- END FORM -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- content-wrapper ends -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- page-body-wrapper ends -->
|
|
||||||
</div>
|
<!-- JS -->
|
||||||
<!-- container-scroller -->
|
|
||||||
<!-- plugins:js -->
|
|
||||||
<script src="{{ asset('assets/admin/vendors/js/vendor.bundle.base.js') }}"></script>
|
<script src="{{ asset('assets/admin/vendors/js/vendor.bundle.base.js') }}"></script>
|
||||||
<!-- endinject -->
|
|
||||||
<!-- Plugin js for this page -->
|
|
||||||
<!-- End plugin js for this page -->
|
|
||||||
<!-- inject:js -->
|
|
||||||
<script src="{{ asset('assets/admin/js/off-canvas.js') }}"></script>
|
<script src="{{ asset('assets/admin/js/off-canvas.js') }}"></script>
|
||||||
<script src="{{ asset('assets/admin/js/hoverable-collapse.js') }}"></script>
|
<script src="{{ asset('assets/admin/js/hoverable-collapse.js') }}"></script>
|
||||||
<script src="{{ asset('assets/admin/js/template.js') }}"></script>
|
<script src="{{ asset('assets/admin/js/template.js') }}"></script>
|
||||||
<script src="{{ asset('assets/admin/js/settings.js') }}"></script>
|
<script src="{{ asset('assets/admin/js/settings.js') }}"></script>
|
||||||
<script src="{{ asset('assets/admin/js/todolist.js') }}"></script>
|
<script src="{{ asset('assets/admin/js/todolist.js') }}"></script>
|
||||||
<!-- endinject -->
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue