首页 / 应用软件 / 手把手教学,为WordPress集成多因素身份验证与登录保护

手把手教学,为WordPress集成多因素身份验证与登录保护

手把手教学:为WordPress集成多因素身份验证与登录保护

引言:为什么WordPress需要多因素身份验证?

在当今数字化时代,网络安全已成为网站运营者不可忽视的重要议题。WordPress作为全球最受欢迎的内容管理系统,占据了互联网近43%的网站份额,也因此成为黑客攻击的主要目标。根据Wordfence安全报告,仅2023年就有超过13亿次针对WordPress网站的攻击尝试。

传统的用户名和密码认证方式已不足以应对日益复杂的网络威胁。密码泄露、暴力破解和社会工程学攻击使得单因素认证变得脆弱。多因素身份验证(MFA)通过要求用户提供两种或更多验证因素,极大地增强了账户安全性。本文将手把手教您如何通过WordPress代码二次开发,集成多因素身份验证与登录保护功能,同时实现一些常用互联网小工具功能。

第一部分:多因素身份验证基础与原理

1.1 多因素身份验证的类型

多因素身份验证通常基于以下三种类型的因素组合:

  1. 知识因素:用户知道的信息,如密码、PIN码或安全问题答案
  2. 持有因素:用户拥有的物理设备,如手机、安全密钥或智能卡
  3. 固有因素:用户本身的生物特征,如指纹、面部识别或虹膜扫描

1.2 常见的MFA实现方式

  • 基于时间的一次性密码(TOTP):使用手机应用(如Google Authenticator)生成随时间变化的6位数字代码
  • 短信/邮件验证码:通过短信或电子邮件发送一次性验证码
  • 推送通知:向已认证设备发送登录请求确认
  • 生物识别:使用指纹、面部识别等生物特征
  • 硬件安全密钥:如YubiKey等物理设备

1.3 WordPress安全现状分析

默认情况下,WordPress仅提供基本的用户名和密码认证。虽然有许多插件可以提供MFA功能,但通过代码二次开发实现有以下优势:

  1. 减少插件依赖:降低插件冲突风险
  2. 性能优化:定制化代码通常比通用插件更高效
  3. 完全控制:可以根据具体需求定制功能
  4. 学习价值:深入理解WordPress认证机制

第二部分:开发环境准备与基础设置

2.1 开发环境配置

在开始编码之前,请确保您已准备好以下环境:

  1. 本地开发环境:推荐使用Local by Flywheel、XAMPP或MAMP
  2. 代码编辑器:VS Code、PHPStorm或Sublime Text
  3. 测试WordPress安装:建议使用最新版本的WordPress
  4. 备份机制:确保可以随时恢复原始状态

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 {
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5120.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

工作时间:周一至周五,9:00-17:30,节假日休息
返回顶部