本文目录导读:

在PHP项目中实现账号锁定的自动解锁,通常有以下几种常见方案,我会按照从简单到复杂的顺序为你介绍:
基于时间的自动解锁(推荐)
核心思路
记录锁定时间 + 每次登录时检查时间是否过期。
实现步骤
数据库字段设计
ALTER TABLE `users` ADD COLUMN `login_attempts` int(11) DEFAULT 0 COMMENT '登录失败次数', ADD COLUMN `locked_until` datetime DEFAULT NULL COMMENT '锁定截止时间';
登录逻辑代码
<?php
// 登录检测
function authenticate($username, $password) {
$user = getUserByUsername($username);
// 检查是否被锁定
if ($user['locked_until'] !== null && strtotime($user['locked_until']) > time()) {
$remaining = strtotime($user['locked_until']) - time();
return [
'success' => false,
'message' => "账号已被锁定,请等待 {$remaining} 秒后重试"
];
}
// 验证密码
if (password_verify($password, $user['password_hash'])) {
// 登录成功:重置失败次数和锁定状态
resetLoginAttempts($user['id']);
return ['success' => true, 'message' => '登录成功'];
} else {
// 登录失败:增加失败次数
$attempts = incrementLoginAttempts($user['id']);
// 达到阈值则锁定(5次失败后锁定15分钟)
$maxAttempts = 5;
$lockDuration = 15 * 60; // 15分钟
if ($attempts >= $maxAttempts) {
lockAccount($user['id'], date('Y-m-d H:i:s', time() + $lockDuration));
return [
'success' => false,
'message' => "登录失败次数过多,账号已被锁定 {$lockDuration} 分钟"
];
}
$remaining = $maxAttempts - $attempts;
return [
'success' => false,
'message' => "密码错误,还有 {$remaining} 次尝试机会"
];
}
}
// 辅助函数
function resetLoginAttempts($userId) {
DB::update("UPDATE users SET login_attempts = 0, locked_until = NULL WHERE id = ?", [$userId]);
}
function incrementLoginAttempts($userId) {
DB::query("UPDATE users SET login_attempts = login_attempts + 1 WHERE id = ?", [$userId]);
return DB::getOne("SELECT login_attempts FROM users WHERE id = ?", [$userId]);
}
function lockAccount($userId, $lockedUntil) {
DB::query("UPDATE users SET locked_until = ? WHERE id = ?", [$lockedUntil, $userId]);
}
?>
缓存自动解锁(Laravel示例)
使用Redis或Memcached存储锁定信息,自动过期。
Laravel实现
<?php
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Hash;
class LoginController
{
const MAX_ATTEMPTS = 5;
const LOCKOUT_DURATION = 15; // 分钟
public function login(Request $request)
{
$key = 'login_attempts_' . $request->username;
// 检查是否锁定
if (Cache::has($key . '_locked')) {
$remaining = Cache::ttl($key . '_locked');
return back()->withErrors([
'username' => "账号已被锁定,请等待 {$remaining} 秒后重试"
]);
}
// 验证凭据
if (Auth::attempt($request->only('username', 'password'))) {
// 清除锁定记录
Cache::forget($key);
Cache::forget($key . '_locked');
return redirect()->intended('/home');
}
// 记录失败次数
$attempts = Cache::increment($key, 1);
if ($attempts >= self::MAX_ATTEMPTS) {
Cache::put($key . '_locked', true, self::LOCKOUT_DURATION * 60);
Cache::forget($key); // 重置计数
return back()->withErrors([
'username' => "登录失败次数过多,账号已被锁定 " . self::LOCKOUT_DURATION . " 分钟"
]);
}
$remaining = self::MAX_ATTEMPTS - $attempts;
return back()->withErrors([
'password' => "密码错误,还有 {$remaining} 次尝试机会"
]);
}
}
渐进式锁定时间(更安全)
实现策略
<?php
function getLockoutDuration($attempts) {
// 第5次失败:15分钟;第10次:1小时;第15次+:24小时
if ($attempts >= 15) return 24 * 3600; // 24小时
if ($attempts >= 10) return 3600; // 1小时
return 15 * 60; // 15分钟
}
function authenticate($username, $password) {
$user = getUserByUsername($username);
// 检查锁定
if ($user['locked_until'] && strtotime($user['locked_until']) > time()) {
return ['success' => false, 'message' => '账号已锁定'];
}
if (verifyPassword($password, $user['password_hash'])) {
resetAttempts($user['id']);
return ['success' => true];
} else {
$attempts = incrementAndGetAttempts($user['id']);
// 计算新的锁定时间
$lockDuration = getLockoutDuration($attempts);
lockUser($user['id'], $lockDuration);
return ['success' => false, 'attempts' => $attempts];
}
}
?>
手动解锁 + 自动检测
管理员端手动解锁
<?php
// 管理员手动解锁
function adminUnlock($userId) {
DB::query("UPDATE users SET login_attempts = 0, locked_until = NULL,
locked_by_admin = 0, unlock_reason = NULL
WHERE id = ?", [$userId]);
}
// 自动检测脚本(Cron Job)
// 每分钟运行一次:reset_locked_accounts.php
function autoUnlockAccounts() {
// 解锁所有已过期的锁定
DB::query("UPDATE users SET login_attempts = 0, locked_until = NULL
WHERE locked_until IS NOT NULL
AND locked_until <= NOW()
AND locked_by_admin = 0");
echo "已解锁 " . DB::affectedRows() . " 个账号";
}
?>
# crontab配置
*/1 * * * * php /path/to/reset_locked_accounts.php
最佳实践建议
安全性增强
<?php
// 添加CAPTCHA验证(推荐)
if ($attempts >= 3) {
require_captcha(); // 要求输入验证码
}
// IP限制(防止暴力破解)
$ip = $_SERVER['REMOTE_ADDR'];
$ipAttempts = Cache::get('ip_attempts_' . $ip, 0);
if ($ipAttempts > 20) { // 同一IP尝试20次
blockIp($ip, 30); // 封锁30分钟
}
?>
用户通知
<?php
// 当解锁时通知用户
function notifyUserOnUnlock($userId) {
$user = getUserById($userId);
if ($user['locked_until'] && strtotime($user['locked_until']) <= time()) {
sendEmail($user['email'], '账号已解锁', '您的账号已自动解锁');
}
}
// 在检查锁定时调用
if ($user['locked_until'] && strtotime($user['locked_until']) <= time()) {
autoUnlockUser($userId);
notifyUserOnUnlock($userId);
}
?>
安全配置建议
<?php
// config.php
return [
'login' => [
'max_attempts' => 5, // 最大尝试次数
'lockout_time' => 15, // 锁定时间(分钟)
'attempts_window' => 30, // 计数窗口(分钟)
'unlock_after' => 180, // 管理员手动解锁后自动解锁(分钟)
'send_notification' => true, // 是否发送通知
]
];
?>
推荐方案
对于大多数项目,方案一(数据库时间戳) + 方案四(定时任务清理) 的组合是最实用的:
- 数据库存储:
login_attempts,locked_until字段 - 登录时自动检测时间是否过期
- 配合Cron Job清理过期的锁定记录
- 管理员可手动解锁特殊账号
这样既简单可靠,又不会增加额外的外部依赖(如Redis)。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播