本文转自:天海博客
“纸上得来终觉浅,绝知此事要躬行。” —— 陆游《冬夜读书示子聿》
在互联网的世界里,网站就像一座城堡,而登录界面则是它的正门。如果这扇门没有锁好,黑客就像“潜入者”,随时可能破门而入。
为了防止机器人、暴力破解等恶意行为,我们可以在登录页加入一个数学题验证码功能。今天,就让我们一起动手实现这个小功能——即使你是编程新手,也能一步步完成!
***
我们要做的,是在 WordPress 默认登录页面 上添加一个简单的验证码功能:
***
WordPress 是一个“可扩展”的系统,就像乐高积木,你可以通过插件给它加新功能。
我们不会改动 WordPress 的核心文件,而是创建一个自己的插件,这样既安全又容易维护。
本教程使用的是 PHP,因为 WordPress 就是用 PHP 写的。只要了解以下几点就够了:
session_start():开启会话,保存验证码结果imagepng():动态生成图片add_action() 和 add_filter():这是 WordPress 的钩子系统,可以理解为“插入自定义代码的位置”有了这些工具,你就可以开始搭建你的“数字堡垒”了。
***
验证码的核心逻辑其实很简单,就像门卫查身份证一样:
整个过程就像是设置了一道“身份验证关卡”,确保来的不是机器人,而是真实用户。
***
进入你的 WordPress 安装目录:/wp-content/plugins/
captcha-logincaptcha-login.php<?php
/*
Plugin Name: Captcha for Login
Description: 在 WordPress 登录页添加数学验证码功能
Version: 1.0
Author: Your Name
*/
if (!defined('ABSPATH')) {
exit; // 禁止直接访问
}
// 加载核心逻辑文件
include plugin_dir_path(__FILE__) . 'includes/functions.php';恭喜你,已经完成了第一步!
***
在插件目录下新建一个文件夹:/captcha-login/includes/
然后创建 functions.php 文件,写入以下代码:
<?php
if (!defined('ABSPATH')) {
exit;
}
// 启动Session
add_action('init','start_session');
function start_session() {
if (!session_id()) {
@session_start();
}
}
// 添加验证码字段到登录表单
add_action('login_form', 'display_captcha_on_login');
function display_captcha_on_login() {
echo '<p>
<label for="operation_captcha">验证码结果为:</label><br />
<input type="text" name="operation_captcha" id="operation_captcha" size="20" tabindex="30" />
<a href="#" id="refresh-captcha"><img src="' . esc_url(plugins_url('refresh.png', __FILE__)) . '" alt="刷新验证码" /></a>
<img src="' . esc_url(home_url('/captcha.png?t='. time())) . '" alt="验证码图片" style="vertical-align:middle;" />
</p>';
echo '<script>
document.getElementById("refresh-captcha").addEventListener("click", function(e) {
e.preventDefault();
var img = document.querySelector("img[alt=验证码图片]");
img.src = "' . esc_url(home_url('/captcha.png?t='. time())) . '";
});
</script>';
}
// 注册重写规则以隐藏真实路径
add_action('init','captcha_rewrite_rule');
function captcha_rewrite_rule() {
add_rewrite_rule('^captcha.png$', 'index.php?__captcha=1', 'top');
}
// 添加自定义查询变量
add_filter('query_vars','captcha_query_var');
function captcha_query_var($vars) {
$vars[] = '__captcha';
return $vars;
}
// 输出验证码图片
add_action('template_redirect','captcha_image');
function captcha_image() {
if (get_query_var('__captcha')) {
include_once plugin_dir_path(__FILE__) . '/captcha.php';
exit;
}
}
// 验证用户输入的验证码
add_filter('authenticate','validate_captcha_login', 10, 3);
function validate_captcha_login($user,$username,$password) {
// 只有在提交登录表单时才验证验证码
if (!isset($_POST['log']) || !isset($_POST['pwd'])) {
return $user;
}
//+ 空字符串检查
$name = $_POST['operation_captcha'] ?? '';
if ($name === '') {
wp_redirect(wp_login_url() . '?cptchaerror=null');
exit;
}
//生成验证码运算结果检查
if (!isset($_SESSION['mcaptcha_result'])) {
wp_redirect(wp_login_url() . '?cptchaerror=resultnull');
exit;
}
//验证码有效期检查
if (isset($_SESSION['captcha_expiry']) && time() > $_SESSION['captcha_expiry']) {
wp_redirect(wp_login_url() . '?cptchaerror=expired');
exit;
}
$submitted = intval($_POST['operation_captcha']);
$correct = isset($_SESSION['captcha_result']) ? intval($_SESSION['captcha_result']) : null;
//验证码校验
if ($submitted !== $correct) {
wp_redirect(wp_login_url() . '?cptchaerror=wrong');
exit;
}
// 验证码通过后,继续执行 WordPress 默认的登录验证
return wp_authenticate_username_password(null, $username, $password);
}
// 在wp-login.php页面显示错误信息
add_action('login_message', 'display_captcha_error');
function display_captcha_error($message) {
if (isset($_GET['cptchaerror'])) {
if ($_GET['cptchaerror'] === 'null' || $_GET['cptchaerror'] === 'resultnull') {
$message .= '<div class="message error"><p><strong>ERROR</strong>: 请输入验证码。</p></div>';
} elseif ($_GET['cptchaerror'] === 'wrong') {
$message .= '<div class="message error"><p><strong>ERROR</strong>: 验证码不正确,请重试。</p></div>';
} elseif ($_GET['cptchaerror'] === 'expired') {
$message .= '<div class="message error"><p><strong>ERROR</strong>: 验证码已过期,请刷新重试。</p></div>';
}
}
return $message;
}
// 清理Session数据
add_action('wp_logout','clear_captcha_session');
add_action('wp_login','clear_captcha_session');
function clear_captcha_session() {
if (isset($_SESSION['captcha_result'])) {
unset($_SESSION['captcha_result']);
}
if (isset($_SESSION['captcha_expiry'])) {
unset($_SESSION['mcaptcha_expiry']);
}
if (isset($_SESSION['captcha_question'])) {
unset($_SESSION['captcha_question']);
}
}📌 该文件拥有以下功能:
***
在/captcha-login/includes/路径下创建 captcha.php 文件,写入以下代码:
<?php
header("Content-type: image/png");
//写一个函数,让它自动出题
function math_Operation_captcha() {
$operators = ['+', '-', '×', '÷'];
$op = $operators[array_rand($operators)];
$num1 = rand(1, 10);
$num2 = rand(1, 10);
switch ($op) {
case '+':
$result = $num1 + $num2;
break;
case '-':
$result = $num1 - $num2;
break;
case '×':
$result = $num1 * $num2;
break;
case '÷':
while ($num2 == 0 || $num1 % $num2 != 0) {
$num1 = rand(1, 10);
$num2 = rand(1, 10);
}
$result = $num1 / $num2;
break;
}
//返回一个类似 "5 × 7" 的题目,并把正确答案保存在 $_SESSION 中,供后续验证使用。
$_SESSION['captcha_result'] = $result;
$_SESSION['captcha_question'] = "$num1 $op $num2";
$_SESSION['captcha_expiry'] = time() + 60; // 验证码有效期 60 秒
return "$num1 $op $num2";
}
$im = imagecreatetruecolor(120, 40);
$bg_color = imagecolorallocate($im, 255, 255, 255); // 白色背景
$text_color = imagecolorallocate($im, 0, 0, 0); // 黑色文字
$line_color = imagecolorallocate($im, 192, 192, 192); // 灰色干扰线
$noise_color = imagecolorallocate($im, 220, 220, 220); // 噪点颜色
imagefilledrectangle($im, 0, 0, 119, 39, $bg_color);
// 干扰线
for ($i = 0; $i < 3; $i++) {
imageline($im, rand(0, 120), 0, rand(0, 120), 40, $line_color);
}
// 噪点
for ($i = 0; $i < 50; $i++) {
imagesetpixel($im, rand(0, 120), rand(0, 40), $noise_color);
}
// 获取图片内容
$question =mc_generate_math_captcha();
// 使用SimHei.ttf字体防止图片内容乱码
imagettftext($im, 14, 0, 10, 22, $text_color, plugin_dir_path(__FILE__) . '/fonts/SimHei.ttf', $question);
imagepng($im);
imagedestroy($im);这段代码会生成一张带干扰线的验证码图片,看起来更专业也更安全。
在includes/路径下创建文件夹fonts,将SimHei.ttf字体文件放入fonts文件夹中。
***
让用户能手动刷新验证码,就像换一道题再答一次:
上传一张名为 refresh.png 的刷新图片到/captcha-login/includes/路径下,注意该图片大小要适配自己验证码图片的大小,否则就不美观。

session_start() 前不能有任何输出(包括空格)init 钩子中启动 Session@session_start() 抑制警告GD 扩展WP_Error不生效或者返回了 $userwp_redirect() 跳转并结束脚本执行(exit)?t=1234567890***
回顾一下我们做了什么:
每一步都像是搭积木,最终拼成了一座坚固的“安全之塔”。
***
虽然整个过程有点复杂,但只要你一步步跟着做,你会发现编写 WordPress 插件并没有想象中那么难。
一旦你完成了这个验证码功能,你就掌握了 WordPress 插件开发的精髓。
***
define('WP_DEBUG', true);/wp-content/debug.logerror_log("验证码不正确") 记录调试信息本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。