需求拆解
- 登錄頁
login.html,連續輸入錯誤 3 次跳轉 nologin.html;
- 進入
nologin.html 後 5 分鐘內訪問 login.html 自動導向 wait.html;
- 5 分鐘後恢復正常登錄;
- 純前端實現,使用 Cookie 記錄狀態。
核心邏輯
login.html:錯誤計數器 errCount,達 3 次寫入 Cookie lockUntil=timestamp+5min 並跳轉;
login.html 加載時檢查 Cookie,若鎖定未過期 → location.href='wait.html';
nologin.html:僅提示頁,無需額外邏輯;
wait.html:倒計時顯示剩余鎖定時間,定時跳回 login.html。
完整代碼
login.html 關鍵腳本
<script>
const LOCK_COOKIE = 'loginLockUntil';
const MAX_ERR = 3;
const LOCK_MIN = 5;
function getLockUntil() {
const m = document.cookie.match(new RegExp('(^| )' + LOCK_COOKIE + '=([^;]+)'));
return m ? parseInt(m[2], 10) : 0;
}
function setLock() {
const until = Date.now() + LOCK_MIN * 60 * 1000;
document.cookie = LOCK_COOKIE + '=' + until + ';path=/;max-age=' + (LOCK_MIN * 60);
}
function checkLock() {
const until = getLockUntil();
if (until && Date.now() < until) {
location.href = 'wait.html';
return true;
}
return false;
}
// 頁面載入即檢查
if (checkLock()) { /* 已跳轉 */ }
let errCount = 0;
function check() {
if (errCount >= MAX_ERR) return; // 已鎖定,checkLock 已攔截
const name = document.getElementById('name').value;
const pass = document.getElementById('pass').value;
if (name === '賬號' && pass === '密碼') {
errCount = 0;
alert('登入成功');
document.forms[0].action = 'in.html';
document.forms[0].submit();
} else {
errCount++;
alert('用戶名或密碼錯誤,連續錯誤 ' + errCount + ' 次');
if (errCount === MAX_ERR) {
setLock();
location.href = 'nologin.html';
}
}
}
</script>
wait.html 倒計時
<script>
const LOCK_COOKIE = 'loginLockUntil';
function tick() {
const until = getLockUntil();
const left = Math.max(0, Math.ceil((until - Date.now()) / 1000));
document.getElementById('countdown').textContent =
Math.floor(left/60) + ':' + String(left%60).padStart(2,'0');
if (left > 0) setTimeout(tick, 1000);
else location.href = 'login.html';
}
function getLockUntil() { /* 同 login.html */ }
tick();
</script>
安全性提示
- 前端鎖定**僅為體驗層阻擋**,無法防禦繞過 JS 的惡意請求;
- 生產環境**必須**在後端同步記錄失敗次數、IP 封禁、驗證碼等措施;
- Cookie 易被清除/篡改,關鍵業務請用 Server-Side Session / Redis 存儲鎖定狀態。