先接一张独立表单
下面是一个最小 WordPress 插件示例,提供 [cap_contact] 短代码。它不会自动保护登录、注册、评论,也不替代其他表单插件的提交钩子。
1. 配置公开 key 和私密 secret
在控制台加入 WordPress 页面实际使用的域名。在服务器的 wp-config.php 中配置下面两项,放在停止编辑的提示之前。不要把 secret 写入主题模板、网页脚本或浏览器公开的环境变量。
wp-config.php
define('CAP_SITEKEY', 'YOUR_SITEKEY');
define('CAP_SECRET', 'YOUR_SERVER_SECRET');2. 安装示例并插入短代码
下载文件,放入 wp-content/plugins/cap-contact/cap-contact.php,在插件列表启用。编辑页面,加入短代码区块 [cap_contact]。
cap-contact.php下载文件 ↓
<?php
/**
* Plugin Name: cap Contact Example
* Description: A minimal shortcode form with server-side cap verification.
*/
if (!defined('ABSPATH')) { exit; }
add_shortcode('cap_contact', function () {
if (!defined('CAP_SITEKEY')) { return '<p>请先配置 CAP_SITEKEY。</p>'; }
wp_enqueue_script('cap-widget', 'https://cap.bug.im/v1.js', array(), null, true);
ob_start(); ?>
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
<input type="hidden" name="action" value="cap_contact">
<?php wp_nonce_field('cap_contact', 'cap_nonce'); ?>
<label>留言 <textarea name="message" maxlength="2000" required></textarea></label>
<div class="cap-captcha" data-sitekey="<?php echo esc_attr(CAP_SITEKEY); ?>"></div>
<button type="submit">提交</button>
</form>
<?php return ob_get_clean();
});
add_action('admin_post_cap_contact', 'cap_contact_handle');
add_action('admin_post_nopriv_cap_contact', 'cap_contact_handle');
function cap_contact_handle() {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { wp_die('仅接受 POST。', '', array('response' => 405)); }
$nonce = isset($_POST['cap_nonce']) && is_string($_POST['cap_nonce']) ? sanitize_text_field(wp_unslash($_POST['cap_nonce'])) : '';
if (!wp_verify_nonce($nonce, 'cap_contact')) { wp_die('表单已失效,请刷新。', '', array('response' => 403)); }
if (!defined('CAP_SECRET')) { wp_die('服务端尚未配置。', '', array('response' => 503)); }
$token = isset($_POST['cap-response']) && is_string($_POST['cap-response']) ? wp_unslash($_POST['cap-response']) : '';
$message = isset($_POST['message']) && is_string($_POST['message']) ? sanitize_textarea_field(wp_unslash($_POST['message'])) : '';
if (!$token || !$message || strlen($message) > 8000 || strlen($token) > 16384) { wp_die('请填写留言并完成验证。', '', array('response' => 400)); }
$check = wp_remote_post('https://cap.bug.im/v1/siteverify', array(
'timeout' => 8,
'headers' => array('Content-Type' => 'application/json'),
'body' => wp_json_encode(array('secret' => CAP_SECRET, 'response' => $token)),
));
if (is_wp_error($check) || wp_remote_retrieve_response_code($check) !== 200) { wp_die('暂时无法校验,请重新验证。', '', array('response' => 503)); }
$result = json_decode(wp_remote_retrieve_body($check), true);
if (!is_array($result) || ($result['success'] ?? false) !== true || !empty($result['degraded'])) { wp_die('验证未通过,请重新验证。', '', array('response' => 403)); }
// Persist the sanitized message here, before displaying success.
wp_die('验证通过。此示例未保存留言。', '', array('response' => 200));
}
3. 在真正处理业务前校验
示例同时处理登录用户和未登录访客的提交,先检查 nonce 与输入,再由服务器请求验证接口。网络错误、失败结果或降级放行都会停止处理。成功响应只表示验证通过,示例不会发送邮件或保存留言。
使用页面缓存时,确认 nonce 不会因长时间缓存而失效。WordPress nonce 不能代替身份认证或限流。若要保护评论或现成表单插件,需要在它们各自的服务端提交钩子中校验,不能只插入控件。
相关接口:登录用户的提交钩子、访客提交钩子、wp_remote_post。