Commit 341273e3 by Alfiro Pratama

add captcha at login form

parent 006873c0
<!DOCTYPE html>
<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8">
<meta charset="utf-8"> <title>Login | SIM PMW UNESA</title>
<title>Login | SIM PMW UNESA</title> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta content="Premium Multipurpose Admin & Dashboard Template" name="description">
<meta content="Premium Multipurpose Admin & Dashboard Template" name="description"> <meta content="Themesbrand" name="author">
<meta content="Themesbrand" name="author"> <link rel="shortcut icon" href="{{ asset('theme/images/favicon.ico') }}">
<!-- App favicon --> <link href="{{ asset('theme/css/bootstrap.min.css') }}" rel="stylesheet">
<link rel="shortcut icon" href="{{ asset('theme/images/favicon.ico') }}"> <link href="{{ asset('theme/css/icons.min.css') }}" rel="stylesheet">
<link href="{{ asset('theme/css/app.min.css') }}" rel="stylesheet">
<!-- Bootstrap Css -->
<link href="{{ asset('theme/css/bootstrap.min.css') }}" id="bootstrap-style" rel="stylesheet" type="text/css"> <!-- Font Awesome -->
<!-- Icons Css --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<link href="{{ asset('theme/css/icons.min.css') }}" rel="stylesheet" type="text/css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css">
<!-- App Css-->
<link href="{{ asset('theme/css/app.min.css') }}" id="app-style" rel="stylesheet" type="text/css"> <style>
/* Captcha styling */
</head> .captcha-container {
position: relative;
background: #f8f9fa;
border: 1px solid #ced4da;
border-radius: 6px;
height: 60px;
overflow: hidden;
margin-bottom: 8px;
user-select: none;
}
.captcha-text {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-family: 'Courier New', monospace;
font-size: 26px;
font-weight: bold;
letter-spacing: 6px;
}
.captcha-text span {
display: inline-block;
transform: rotate(var(--rotation, 0deg));
}
.captcha-noise {
position: absolute;
width: 100%;
height: 100%;
}
.captcha-refresh {
position: absolute;
right: 10px;
top: 10px;
background: transparent;
border: none;
cursor: pointer;
color: #0d6efd;
}
.captcha-refresh:hover {
color: #084298;
}
.input-group-text {
cursor: pointer;
}
</style>
</head>
<body> <body>
<div class="account-pages my-5 pt-5"> <div class="account-pages my-5 pt-5">
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-8 col-lg-6 col-xl-4"> <div class="col-md-8 col-lg-6 col-xl-4">
<div class="card overflow-hidden"> <div class="card overflow-hidden shadow">
<div class="bg-primary"> <div class="bg-primary">
<div class="text-primary text-center p-4"> <div class="text-primary text-center p-4">
<h5 class="text-white font-size-20">LOGIN</h5> <h5 class="text-white font-size-20">LOGIN</h5>
...@@ -39,39 +89,50 @@ ...@@ -39,39 +89,50 @@
<div class="card-body p-4"> <div class="card-body p-4">
<div class="p-3"> <div class="p-3">
<form method="POST" action="{{ route('login') }}"> <form class="auth-login-form" method="POST" action="{{ route('login') }}">
@csrf @csrf
<div class="mb-3"> <div class="mb-3">
<label class="form-label" for="email">Email</label> <label class="form-label" for="email">Email</label>
<input class="form-control" id="email" type="email" name="email" :value="old('email')" required autofocus> <input class="form-control" id="email" type="email" name="email" required autofocus>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label" for="userpassword">Password</label> <label class="form-label" for="password">Password</label>
<input class="form-control" id="password" type="password" name="password" required autocomplete="current-password"> <div class="input-group">
<input class="form-control" id="password" type="password" name="password" required autocomplete="current-password">
<span class="input-group-text" id="passwordToggle">
<i class="fa fa-eye"></i>
</span>
</div>
</div> </div>
<div class="mb-3 row"> {{-- Captcha --}}
<div class="col-sm-6"> <div class="mb-3">
<label class="form-label">Verifikasi Captcha</label>
</div> <div class="captcha-container">
<div class="col-sm-6 text-end"> <div class="captcha-noise"></div>
<button class="btn btn-primary w-md waves-effect waves-light" type="submit">Log In</button> <div class="captcha-text" id="captchaText"></div>
<button type="button" class="captcha-refresh" id="refreshCaptcha" title="Refresh Captcha">
<i class="fa fa-refresh"></i>
</button>
</div> </div>
<input type="text" class="form-control" id="captchaInput" name="captcha"
placeholder="Masukkan kode captcha di atas" maxlength="6" required>
</div> </div>
</form>
<div class="mb-3 text-end">
<button class="btn btn-primary w-md waves-effect waves-light" type="submit">Log In</button>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>
<div class="mt-5 text-center"> <div class="mt-4 text-center">
<p class="mb-0">© <script>document.write(new Date().getFullYear())</script> SIM PMW. Crafted with <i class="mdi mdi-heart text-danger"></i> by PPTI UNESA</p> <p class="mb-0">© <script>document.write(new Date().getFullYear())</script> SIM PMW. Crafted with
<i class="mdi mdi-heart text-danger"></i> by PPTI UNESA</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
...@@ -80,12 +141,112 @@ ...@@ -80,12 +141,112 @@
<!-- JAVASCRIPT --> <!-- JAVASCRIPT -->
<script src="{{ asset('theme/libs/jquery/jquery.min.js') }}"></script> <script src="{{ asset('theme/libs/jquery/jquery.min.js') }}"></script>
<script src="{{ asset('theme/libs/bootstrap/js/bootstrap.bundle.min.js') }}"></script> <script src="{{ asset('theme/libs/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<script src="{{ asset('theme/libs/metismenu/metisMenu.min.js') }}"></script>
<script src="{{ asset('theme/libs/simplebar/simplebar.min.js') }}"></script>
<script src="{{ asset('theme/libs/node-waves/waves.min.js') }}"></script>
<script src="{{ asset('theme/js/app.js') }}"></script> <script src="{{ asset('theme/js/app.js') }}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
<script>
// Toastr Configuration
toastr.options = {
"closeButton": true, // Show close button
"progressBar": true, // Show progress bar
"newestOnTop": true, // Newest notifications on top
"positionClass": "toast-top-right",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
</script>
<script>
$(function () {
'use strict';
let captchaText = '';
// 🔄 Generate Captcha
function generateCaptcha() {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
captchaText = '';
let html = '';
for (let i = 0; i < 6; i++) {
const char = chars.charAt(Math.floor(Math.random() * chars.length));
captchaText += char;
const rotation = (Math.random() - 0.5) * 25;
const color = '#' + Math.floor(Math.random() * 16777215).toString(16);
html += `<span style="--rotation:${rotation}deg;color:${color};">${char}</span>`;
}
$('#captchaText').html(html);
sessionStorage.setItem('captcha_text', captchaText);
addNoise();
}
// 🌫 Tambahkan titik noise acak
function addNoise() {
const noise = $('.captcha-noise');
noise.empty();
for (let i = 0; i < 15; i++) {
const dot = $('<div>').css({
position: 'absolute',
left: Math.random() * 100 + '%',
top: Math.random() * 100 + '%',
width: (Math.random() * 2 + 1) + 'px',
height: (Math.random() * 2 + 1) + 'px',
backgroundColor: 'rgba(0,0,0,0.2)',
borderRadius: '50%'
});
noise.append(dot);
}
}
// 🔁 Refresh captcha
$('#refreshCaptcha').click(function () {
generateCaptcha();
$('#captchaInput').val('').focus();
});
// 👁 Toggle password (Font Awesome)
$('#passwordToggle').on('click', function () {
const passwordField = $('#password');
const icon = $(this).find('i');
const isPassword = passwordField.attr('type') === 'password';
passwordField.attr('type', isPassword ? 'text' : 'password');
icon.removeClass(isPassword ? 'fa-eye' : 'fa-eye-slash')
.addClass(isPassword ? 'fa-eye-slash' : 'fa-eye');
});
// 🔤 Auto uppercase captcha input
$('#captchaInput').on('input', function () {
const cursorPos = this.selectionStart;
this.value = this.value.toUpperCase();
this.setSelectionRange(cursorPos, cursorPos);
});
// 🚫 Prevent copy-paste on captcha
$('#captchaInput').on('paste copy cut', function (e) {
e.preventDefault();
toastr.warning('Copy-paste tidak diizinkan pada captcha!');
});
// ✅ Validasi saat submit
$('form').on('submit', function (e) {
const input = $('#captchaInput').val().toUpperCase();
const stored = sessionStorage.getItem('captcha_text');
if (input !== stored) {
e.preventDefault();
toastr.error('Captcha salah! Silakan coba lagi.');
generateCaptcha();
$('#captchaInput').val('').focus();
}
});
generateCaptcha(); // initial load
});
</script>
</body> </body>
</html> </html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment