文章目录[隐藏]
手把手教学:为WordPress集成多因素身份验证与登录保护
引言:为什么WordPress需要多因素身份验证?
在当今数字化时代,网络安全已成为网站运营者不可忽视的重要议题。WordPress作为全球最受欢迎的内容管理系统,占据了互联网近43%的网站份额,也因此成为黑客攻击的主要目标。根据Wordfence安全报告,仅2023年就有超过13亿次针对WordPress网站的攻击尝试。
传统的用户名和密码认证方式已不足以应对日益复杂的网络威胁。密码泄露、暴力破解和社会工程学攻击使得单因素认证变得脆弱。多因素身份验证(MFA)通过要求用户提供两种或更多验证因素,极大地增强了账户安全性。本文将手把手教您如何通过WordPress代码二次开发,集成多因素身份验证与登录保护功能,同时实现一些常用互联网小工具功能。
第一部分:多因素身份验证基础与原理
1.1 多因素身份验证的类型
多因素身份验证通常基于以下三种类型的因素组合:
- 知识因素:用户知道的信息,如密码、PIN码或安全问题答案
- 持有因素:用户拥有的物理设备,如手机、安全密钥或智能卡
- 固有因素:用户本身的生物特征,如指纹、面部识别或虹膜扫描
1.2 常见的MFA实现方式
- 基于时间的一次性密码(TOTP):使用手机应用(如Google Authenticator)生成随时间变化的6位数字代码
- 短信/邮件验证码:通过短信或电子邮件发送一次性验证码
- 推送通知:向已认证设备发送登录请求确认
- 生物识别:使用指纹、面部识别等生物特征
- 硬件安全密钥:如YubiKey等物理设备
1.3 WordPress安全现状分析
默认情况下,WordPress仅提供基本的用户名和密码认证。虽然有许多插件可以提供MFA功能,但通过代码二次开发实现有以下优势:
- 减少插件依赖:降低插件冲突风险
- 性能优化:定制化代码通常比通用插件更高效
- 完全控制:可以根据具体需求定制功能
- 学习价值:深入理解WordPress认证机制
第二部分:开发环境准备与基础设置
2.1 开发环境配置
在开始编码之前,请确保您已准备好以下环境:
- 本地开发环境:推荐使用Local by Flywheel、XAMPP或MAMP
- 代码编辑器:VS Code、PHPStorm或Sublime Text
- 测试WordPress安装:建议使用最新版本的WordPress
- 备份机制:确保可以随时恢复原始状态
2.2 创建自定义插件框架
首先,我们创建一个专门用于MFA功能的插件:
<?php
/**
* Plugin Name: 高级登录保护与MFA
* Plugin URI: https://yourwebsite.com/
* Description: 为WordPress添加多因素身份验证和增强登录保护功能
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('MFA_PLUGIN_VERSION', '1.0.0');
define('MFA_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('MFA_PLUGIN_URL', plugin_dir_url(__FILE__));
// 初始化插件
add_action('plugins_loaded', 'mfa_plugin_init');
function mfa_plugin_init() {
// 检查必要扩展
if (!extension_loaded('gd')) {
add_action('admin_notices', function() {
echo '<div class="notice notice-error"><p>高级登录保护插件需要GD扩展支持,请启用PHP的GD扩展。</p></div>';
});
return;
}
// 加载核心类
require_once MFA_PLUGIN_PATH . 'includes/class-mfa-core.php';
require_once MFA_PLUGIN_PATH . 'includes/class-totp-authenticator.php';
require_once MFA_PLUGIN_PATH . 'includes/class-login-protection.php';
// 初始化核心功能
$mfa_core = MFA_Core::get_instance();
}
第三部分:实现TOTP多因素身份验证
3.1 TOTP原理与实现
基于时间的一次性密码算法是当前最流行的MFA实现方式之一。以下是实现TOTP的核心类:
<?php
// includes/class-totp-authenticator.php
class TOTP_Authenticator {
private $secret_length = 16;
private $time_step = 30; // 时间步长(秒)
private $code_length = 6; // 验证码长度
/**
* 生成随机密钥
*/
public function generate_secret() {
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; // Base32字符集
$secret = '';
for ($i = 0; $i < $this->secret_length; $i++) {
$secret .= $chars[random_int(0, 31)];
}
return $secret;
}
/**
* 生成TOTP验证码
*/
public function generate_code($secret) {
$time = floor(time() / $this->time_step);
$time = pack('J', $time); // 64位大端字节序
$secret = $this->base32_decode($secret);
$hash = hash_hmac('sha1', $time, $secret, true);
// 动态截取
$offset = ord($hash[19]) & 0xF;
$code = (
((ord($hash[$offset]) & 0x7F) << 24) |
((ord($hash[$offset + 1]) & 0xFF) << 16) |
((ord($hash[$offset + 2]) & 0xFF) << 8) |
(ord($hash[$offset + 3]) & 0xFF)
) % pow(10, $this->code_length);
return str_pad($code, $this->code_length, '0', STR_PAD_LEFT);
}
/**
* 验证TOTP代码
*/
public function verify_code($secret, $code, $discrepancy = 1) {
$current_time = floor(time() / $this->time_step);
// 允许时间偏差
for ($i = -$discrepancy; $i <= $discrepancy; $i++) {
$time = pack('J', $current_time + $i);
$secret_decoded = $this->base32_decode($secret);
$hash = hash_hmac('sha1', $time, $secret_decoded, true);
$offset = ord($hash[19]) & 0xF;
$calculated_code = (
((ord($hash[$offset]) & 0x7F) << 24) |
((ord($hash[$offset + 1]) & 0xFF) << 16) |
((ord($hash[$offset + 2]) & 0xFF) << 8) |
(ord($hash[$offset + 3]) & 0xFF)
) % pow(10, $this->code_length);
$calculated_code = str_pad($calculated_code, $this->code_length, '0', STR_PAD_LEFT);
if (hash_equals($calculated_code, $code)) {
return true;
}
}
return false;
}
/**
* Base32解码
*/
private function base32_decode($secret) {
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
$buffer = 0;
$bitsLeft = 0;
$output = '';
for ($i = 0; $i < strlen($secret); $i++) {
$char = $secret[$i];
$value = strpos($chars, $char);
if ($value === false) {
continue;
}
$buffer <<= 5;
$buffer |= $value;
$bitsLeft += 5;
if ($bitsLeft >= 8) {
$bitsLeft -= 8;
$output .= chr(($buffer >> $bitsLeft) & 0xFF);
}
}
return $output;
}
/**
* 生成QR码URL(用于Google Authenticator等应用)
*/
public function generate_qr_url($secret, $username, $issuer = 'WordPress') {
$label = rawurlencode($issuer . ':' . $username);
$parameters = array(
'secret' => $secret,
'issuer' => $issuer,
'digits' => $this->code_length,
'period' => $this->time_step,
);
$url = 'otpauth://totp/' . $label . '?' . http_build_query($parameters);
return $url;
}
}
3.2 用户MFA设置界面
接下来,我们需要为用户创建MFA设置界面:
<?php
// includes/class-mfa-user-settings.php
class MFA_User_Settings {
public function __construct() {
// 在用户个人资料页面添加MFA设置
add_action('show_user_profile', array($this, 'add_mfa_settings_fields'));
add_action('edit_user_profile', array($this, 'add_mfa_settings_fields'));
// 保存MFA设置
add_action('personal_options_update', array($this, 'save_mfa_settings'));
add_action('edit_user_profile_update', array($this, 'save_mfa_settings'));
// 添加AJAX处理
add_action('wp_ajax_generate_mfa_secret', array($this, 'ajax_generate_secret'));
add_action('wp_ajax_verify_mfa_code', array($this, 'ajax_verify_code'));
}
/**
* 在用户资料页面添加MFA设置字段
*/
public function add_mfa_settings_fields($user) {
// 只有管理员或用户自己可以查看
if (!current_user_can('edit_user', $user->ID)) {
return;
}
$mfa_enabled = get_user_meta($user->ID, 'mfa_enabled', true);
$mfa_secret = get_user_meta($user->ID, 'mfa_secret', true);
$backup_codes = get_user_meta($user->ID, 'mfa_backup_codes', true);
// 如果还没有密钥,生成一个
if (empty($mfa_secret)) {
$totp = new TOTP_Authenticator();
$mfa_secret = $totp->generate_secret();
update_user_meta($user->ID, 'mfa_secret', $mfa_secret);
}
?>
<h3>多因素身份验证设置</h3>
<table class="form-table">
<tr>
<th><label for="mfa_enabled">启用MFA</label></th>
<td>
<input type="checkbox" name="mfa_enabled" id="mfa_enabled" value="1" <?php checked($mfa_enabled, '1'); ?> />
<span class="description">启用多因素身份验证以增强账户安全</span>
</td>
</tr>
<?php if ($mfa_enabled): ?>
<tr>
<th>MFA状态</th>
<td>
<span style="color: green; font-weight: bold;">✓ 已启用</span>
<p class="description">您的账户已受到多因素身份验证保护</p>
</td>
</tr>
<?php endif; ?>
<tr>
<th>设置MFA</th>
<td>
<div id="mfa-setup-container">
<ol>
<li>在手机上安装Google Authenticator或类似应用</li>
<li>点击下方按钮生成二维码</li>
<li>使用应用扫描二维码</li>
<li>输入应用生成的6位验证码进行验证</li>
</ol>
<div id="mfa-qr-container" style="display: none;">
<div id="mfa-qr-code"></div>
<p><strong>密钥:</strong> <code id="mfa-secret-text"><?php echo esc_html($mfa_secret); ?></code></p>
<p class="description">如果无法扫描二维码,可以手动输入上方密钥</p>
</div>
<button type="button" id="show-mfa-qr" class="button">显示二维码</button>
<div style="margin-top: 15px;">
<label for="mfa-verify-code">验证码:</label>
<input type="text" id="mfa-verify-code" maxlength="6" style="width: 100px;" />
<button type="button" id="verify-mfa-code" class="button">验证并启用MFA</button>
<span id="mfa-verify-result" style="margin-left: 10px;"></span>
</div>
</div>
</td>
</tr>
<?php if (!empty($backup_codes)): ?>
<tr>
<th>备用验证码</th>
<td>
<p>以下是一次性备用验证码(每个仅能使用一次):</p>
<div style="background: #f5f5f5; padding: 10px; font-family: monospace;">
<?php
foreach ($backup_codes as $code) {
if ($code['used'] == 0) {
echo '<div>' . esc_html($code['code']) . '</div>';
}
}
?>
</div>
<button type="button" id="generate-new-backup-codes" class="button">生成新的备用验证码</button>
</td>
</tr>
<?php endif; ?>
</table>
<script type="text/javascript">
jQuery(document).ready(function($) {
// 显示二维码
$('#show-mfa-qr').on('click', function() {
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'generate_mfa_secret',
user_id: <?php echo $user->ID; ?>,
nonce: '<?php echo wp_create_nonce('mfa_nonce'); ?>'
},
success: function(response) {
if (response.success) {
$('#mfa-qr-container').show();
$('#mfa-qr-code').html('<img src="' + response.data.qr_url + '" />');
$('#mfa-secret-text').text(response.data.secret);
}
}
});
});
// 验证MFA代码
$('#verify-mfa-code').on('click', function() {
var code = $('#mfa-verify-code').val();
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'verify_mfa_code',
user_id: <?php echo $user->ID; ?>,
code: code,
nonce: '<?php echo wp_create_nonce('mfa_nonce'); ?>'
},
success: function(response) {
if (response.success) {
$('#mfa-verify-result').html('<span style="color: green;">✓ 验证成功!MFA已启用。</span>');
$('#mfa_enabled').prop('checked', true);
} else {
$('#mfa-verify-result').html('<span style="color: red;">✗ 验证失败,请重试。</span>');
}
}
});
});
});
</script>
<?php
}
/**
* 保存MFA设置
*/
public function save_mfa_settings($user_id) {
if (!current_user_can('edit_user', $user_id)) {
return false;
}
// 验证nonce
if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'update-user_' . $user_id)) {
return false;
}
// 保存MFA启用状态
if (isset($_POST['mfa_enabled'])) {
update_user_meta($user_id, 'mfa_enabled', '1');
} else {
delete_user_meta($user_id, 'mfa_enabled');
}
return true;
}
/**
* AJAX生成MFA密钥和二维码
*/
public function ajax_generate_secret() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'mfa_nonce')) {
wp_die('安全验证失败');
}
$user_id = intval($_POST['user_id']);
$current_user_id = get_current_user_id();
// 验证权限
if ($user_id != $current_user_id && !current_user_can('edit_user', $user_id)) {
wp_die('权限不足');
}
$totp = new TOTP_Authenticator();
$secret = get_user_meta($user_id, 'mfa_secret', true);
if (empty($secret)) {
$secret = $totp->generate_secret();
update_user_meta($user_id, 'mfa_secret', $secret);
}
$user_info = get_userdata($user_id);
$qr_url = $totp->generate_qr_url($secret, $user_info->user_login, get_bloginfo('name'));
// 生成QR码图片
require_once MFA_PLUGIN_PATH . 'includes/class-qr-generator.php';
$qr_generator = new QR_Generator();
$qr_image_url = $qr_generator->generate($qr_url);
wp_send_json_success(array(
'secret' => $secret,
'qr_url' => $qr_image_url
));
}
/**
* AJAX验证MFA代码
*/
public function ajax_verify_code() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'mfa_nonce')) {
wp_die('安全验证失败');
}
$user_id = intval($_POST['user_id']);
$code = sanitize_text_field($_POST['code']);
$current_user_id = get_current_user_id();
// 验证权限
if ($user_id != $current_user_id && !current_user_can('edit_user', $user_id)) {
wp_die('权限不足');
}
$secret = get_user_meta($user_id, 'mfa_secret', true);
if (empty($secret)) {
wp_send_json_error('未找到MFA密钥');
}
$totp = new TOTP_Authenticator();
$is_valid = $totp->verify_code($secret, $code);
if ($is_valid) {
// 启用MFA
update_user_meta($user_id, 'mfa_enabled', '1');
// 生成备用验证码
$this->generate_backup_codes($user_id);
wp_send_json_success('验证成功');
} else {
wp_send_json_error('验证码无效');
}
}
/**
* 生成备用验证码
*/
private function generate_backup_codes($user_id) {
$backup_codes = array();
for ($i = 0; $i < 10; $i++) {
$code = '';
for ($j = 0; $j < 8; $j++) {
$code .= random_int(0, 9);
}
$backup_codes[] = array(
'code' => $code,
'used' => 0
);
}
update_user_meta($user_id, 'mfa_backup_codes', $backup_codes);
return $backup_codes;
}
}
### 3.3 集成QR码生成功能
<?php
// includes/class-qr-generator.php
class QR_Generator {
public function generate($data, $size = 200) {
// 使用PHP GD库生成QR码
if (!function_exists('imagecreate')) {
return $this->generate_via_api($data, $size);
}
// 简单QR码实现(实际项目中建议使用专业库如phpqrcode)
$qr_size = $size;
$margin = 10;
$module_size = ($qr_size - 2 * $margin) / 21; // 简单QR码版本
$image = imagecreate($qr_size, $qr_size);
// 颜色
$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
// 填充白色背景
imagefilledrectangle($image, 0, 0, $qr_size, $qr_size, $white);
// 生成简单QR码图案(实际应使用专业算法)
$this->draw_simple_qr($image, $data, $margin, $module_size, $black);
// 保存临时文件
$upload_dir = wp_upload_dir();
$temp_filename = 'qr_' . md5($data . time()) . '.png';
$temp_path = $upload_dir['path'] . '/' . $temp_filename;
$temp_url = $upload_dir['url'] . '/' . $temp_filename;
imagepng($image, $temp_path);
imagedestroy($image);
// 清理旧文件
$this->cleanup_old_qr_files();
return $temp_url;
}
private function draw_simple_qr($image, $data, $margin, $module_size, $color) {
// 简化的QR码绘制逻辑
// 实际项目中应使用专业QR码生成库
$hash = md5($data);
$hash_binary = '';
// 将哈希转换为二进制字符串
for ($i = 0; $i < strlen($hash); $i++) {
$char = $hash[$i];
$binary = str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
$hash_binary .= $binary;
}
// 绘制简单的QR码图案
$bits = str_split($hash_binary);
$bit_index = 0;
for ($y = 0; $y < 21; $y++) {
for ($x = 0; $x < 21; $x++) {
if ($bit_index >= count($bits)) {
$bit_index = 0;
}
$bit = $bits[$bit_index];
$bit_index++;
if ($bit == '1') {
$x_pos = $margin + $x * $module_size;
$y_pos = $margin + $y * $module_size;
imagefilledrectangle(
$image,
$x_pos,
$y_pos,
$x_pos + $module_size,
$y_pos + $module_size,
$color
);
}
}
}
// 添加定位标记(简化版)
$this->draw_finder_pattern($image, $margin, $margin, $module_size, $color);
$this->draw_finder_pattern($image, $margin + 14 * $module_size, $margin, $module_size, $color);
$this->draw_finder_pattern($image, $margin, $margin + 14 * $module_size, $module_size, $color);
}
private function draw_finder_pattern($image, $x, $y, $module_size, $color) {
// 绘制7x7的定位标记
for ($i = 0; $i < 7; $i++) {
for ($j = 0; $j < 7; $j++) {
if ($i == 0 || $i == 6 || $j == 0 || $j == 6 ||
($i >= 2 && $i <= 4 && $j >= 2 && $j <= 4)) {
$x_pos = $x + $i * $module_size;
$y_pos = $y + $j * $module_size;
imagefilledrectangle(
$image,
$x_pos,
$y_pos,
$x_pos + $module_size,
$y_pos + $module_size,
$color
);
}
}
}
}
private function generate_via_api($data, $size) {
// 使用外部API生成QR码(备用方案)
$encoded_data = urlencode($data);
$api_url = "https://api.qrserver.com/v1/create-qr-code/?size={$size}x{$size}&data={$encoded_data}";
return $api_url;
}
private function cleanup_old_qr_files() {
$upload_dir = wp_upload_dir();
$files = glob($upload_dir['path'] . '/qr_*.png');
// 删除超过1小时的旧文件
$now = time();
foreach ($files as $file) {
if ($now - filemtime($file) > 3600) {
@unlink($file);
}
}
}
}
## 第四部分:增强登录保护机制
### 4.1 登录尝试限制与IP封锁
<?php
// includes/class-login-protection.php
class Login_Protection {
private $max_attempts = 5;
private $lockout_time = 900; // 15分钟
private $max_lockouts = 3;
private $extended_lockout_time = 86400; // 24小时
public function __construct() {
// 登录验证钩子
add_filter('authenticate', array($this, 'check_login_attempts'), 30, 3);
// 登录成功/失败钩子
add_action('wp_login_failed', array($this, 'record_failed_attempt'));
add_action('wp_login', array($this, 'clear_login_attempts'));
// 添加登录页面保护
add_action('login_form', array($this, 'add_login_protection'));
// 添加AJAX处理
add_action('wp_ajax_nopriv_check_login_lock', array($this, 'ajax_check_login_lock'));
}
/**
* 检查登录尝试次数
*/
public function check_login_attempts($user, $username, $password) {
// 如果已经通过其他方式验证,直接返回
if (is_wp_error($user) && $user->get_error_code() !== 'empty_username' &&
$user->get_error_code() !== 'empty_password') {
return $user;
}
$ip = $this->get_client_ip();
$attempts = $this->get_login_attempts($ip);
// 检查是否被封锁
if ($this->is_ip_locked($ip)) {
$lockout_time = $this->get_lockout_time($ip);
$remaining = $lockout_time - time();
return new WP_Error(
'ip_locked',
sprintf(
'登录尝试次数过多。请等待 %d 分 %d 秒后再试。',
floor($remaining / 60),
$remaining % 60
)
);
}
// 检查尝试次数
if ($attempts >= $this->max_attempts) {
$this->lock_ip($ip);
return new WP_Error(
'too_many_attempts',
'登录尝试次数过多。您的IP已被暂时封锁。'
);
}
return $user;
}
/**
* 记录失败尝试
*/
public function record_failed_attempt($username) {
$ip = $this->get_client_ip();
$attempts = $this->get_login_attempts($ip);
$attempts++;
$this->set_login_attempts($ip, $attempts);
// 记录详细信息
$this->log_failed_attempt($ip, $username);
}
/**
* 清除登录尝试记录
*/
public function clear_login_attempts($username) {
$ip = $this->get_client_ip();
$this->set_login_attempts($ip, 0);
$this->clear_ip_lock($ip);
}
/**
* 获取客户端IP
*/
private function get_client_ip() {
$ip_keys = array(
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR'
);
foreach ($ip_keys as $key) {
if (array_key_exists($key, $_SERVER) === true) {
foreach (explode(',', $_SERVER[$key]) as $ip) {
$ip = trim($ip);
// 验证IP地址
if (filter_var($ip, FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
return $ip;
}
}
}
}
return $_SERVER['REMOTE_ADDR'];
}
/**
* 获取登录尝试次数
*/
private function get_login_attempts($ip) {
$transient_key = 'login_attempts_' . md5($ip);
$attempts = get_transient($transient_key);
return $attempts ? intval($attempts) : 0;
}
/**
* 设置登录尝试次数
*/
private function set_login_attempts($ip, $attempts) {
$transient_key = 'login_attempts_' . md5($ip);
$expiration = 3600; // 1小时
set_transient($transient_key, $attempts, $expiration);
}
/**
* 封锁IP
*/
private function lock_ip($ip) {
$lockout_key = 'ip_lockout_' . md5($ip);
$lockout_count_key = 'ip_lockout_count_' . md5($ip);
// 获取当前封锁次数
$lockout_count = get_transient($lockout_count_key);
$lockout_count = $lockout_count ? intval($lockout_count) + 1 : 1;
// 设置封锁时间
$lockout_time = $this->lockout_time;
if ($lockout_count >= $this->max_lockouts) {
$lockout_time = $this->extended_lockout_time;
}
set_transient($lockout_key, time() + $lockout_time, $lockout_time);
set_transient($lockout_count_key, $lockout_count, 86400); // 24小时
// 记录封锁日志
$this->log_ip_lock($ip, $lockout_time, $lockout_count);
}
/**
* 检查IP是否被封锁
*/
private function is_ip_locked($ip) {
$lockout_key = 'ip_lockout_' . md5($ip);
$lockout_time = get_transient($lockout_key);
if ($lockout_time && $lockout_time > time()) {
return true;
}
return false;
}
/**
* 获取封锁剩余时间
*/
private function get_lockout_time($ip) {
$lockout_key = 'ip_lockout_' . md5($ip);
return get_transient($lockout_key);
}
/**
* 清除IP封锁
*/
private function clear_ip_lock($ip) {
$lockout_key = 'ip_lockout_' . md5($ip);
$lockout_count_key = 'ip_lockout_count_' . md5($ip);
delete_transient($lockout_key);
delete_transient($lockout_count_key);
}
/**
* 记录失败尝试日志
*/
private function log_failed_attempt($ip, $username) {
$log_entry = array(
'time' => current_time('mysql'),
'ip' => $ip,
'username' => $username,
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'referer' => $_SERVER['HTTP_REFERER'] ?? ''
);
$log = get_option('mfa_failed_login_log', array());
$log[] = $log_entry;
// 只保留最近100条记录
if (count($log) > 100) {
$log = array_slice($log, -100);
}
update_option('mfa_failed_login_log', $log);
}
/**
* 记录IP封锁日志
*/
private function log_ip_lock($ip, $duration, $count) {
$log_entry = array(
'time' => current_time('mysql'),
'ip' => $ip,
'duration' => $duration,
'count' => $count,
'reason' => 'too_many_failed_attempts'
);
$log = get_option('mfa_ip_lock_log', array());
$log[] = $log_entry;
// 只保留最近50条记录
if (count($log) > 50) {
$log = array_slice($log, -50);
}
update_option('mfa_ip_lock_log', $log);
}
/**
* 添加登录页面保护
*/
public function add_login_protection() {
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// 检查登录锁定状态
function checkLoginLock() {
$.ajax({
url: '<?php echo admin_url('admin-ajax.php'); ?>',
type: 'POST',
data: {
action: 'check_login_lock'
},
success: function(response) {
if (response.locked) {
// 显示锁定消息
$('#login').prepend(
'<div class="message error" style="border-left-color: #dc3232;">' +
'<p>登录尝试次数过多。请等待 ' +
Math.floor(response.remaining / 60) + ' 分 ' +
(response.remaining % 60) + ' 秒后再试。</p>' +
'</div>'
);
// 禁用登录表单
$('#loginform input').prop('disabled', true);
$('#wp-submit').prop('disabled', true);
// 更新倒计时
setTimeout(updateCountdown, 1000);
}
}
});
}
// 更新倒计时显示
function updateCountdown() {
var countdownEl = $('.countdown-timer');
if (countdownEl.length) {
var remaining = parseInt(countdownEl.data('remaining')) - 1;
if (remaining <= 0) {
location.reload();
} else {
countdownEl.data('remaining', remaining);
countdownEl.text(
Math.floor(remaining / 60) + ' 分 ' +
(remaining % 60) + ' 秒'
);
setTimeout(updateCountdown, 1000);
}
}
}
// 页面加载时检查
checkLoginLock();
});
</script>
<?php
}
/**
* AJAX检查登录锁定状态
*/
public function ajax_check_login_lock() {
$ip = $this->get_client_ip();
if ($this->is_ip_locked($ip)) {
$lockout_time = $this->get_lockout_time($ip);
$remaining = $lockout_time - time();
wp_send_json_success(array(
'locked' => true,
'remaining' => $remaining
));
} else {
