准备工作
在开始集成之前,请确保您已准备好以下事项:
- Geetest 账户:注册并登录 Geetest 官方网站。
- Geetest V4 凭证:在 Geetest 后台创建一个新的 V4 验证,并获取其对应的
CAPTCHA_ID
和CAPTCHA_KEY
。
集成原理
我们将采用“后端先行”的验证模式,其核心流程如下:
实施步骤:以登录页面为例
我们将首先以最核心的后台登录页面为例,完整地走通集成流程。
步骤一:修改后端登录逻辑 (var/Widget/Login.php
)
这是集成的核心。我们需要让 Typecho 在处理登录时,先去问询 Geetest 服务器。
- 打开
var/Widget/Login.php
文件。 在
Widget_Login
类中,添加两个新的私有方法:_geetestValidate()
用于处理二次验证逻辑,_sendPost()
用于发送 HTTP 请求。/** * [Geetest] 新增:用于Geetest二次验证的私有方法 */ private function _geetestValidate($lot_number, $captcha_output, $pass_token, $gen_time) { // --- 请在这里填入你的Geetest凭证 --- $captcha_id = '你的_CAPTCHA_ID'; $captcha_key = '你的_CAPTCHA_KEY'; // ------------------------------------ $api_server = 'http://gcaptcha4.geetest.com'; $sign_token = hash_hmac('sha256', $lot_number, $captcha_key); $post_data = [ 'lot_number' => $lot_number, 'captcha_output' => $captcha_output, 'pass_token' => $pass_token, 'gen_time' => $gen_time, 'sign_token' => $sign_token, ]; $url = $api_server . '/validate?captcha_id=' . $captcha_id; $response_json = $this->_sendPost($url, $post_data); return json_decode($response_json, true); } /** * [Geetest] 新增:用于发送POST请求的私有方法 */ private function _sendPost($url, $post_data) { $postdata = http_build_query($post_data); $options = [ 'http' => [ 'method' => 'POST', 'header' => 'Content-type:application/x-www-form-urlencoded', 'content' => $postdata, 'timeout' => 20, ], ]; $context = stream_context_create($options); $result = file_get_contents($url, false, $context); return $result; }
找到
action()
方法,并对其进行修改:public function action() { // ... (前面的代码保持不变) /** 初始化验证类 */ $validator = new Typecho_Validate(); // 或 new Validate(),取决于你的Typecho版本 $validator->addRule('name', 'required', _t('请输入用户名')); $validator->addRule('password', 'required', _t('请输入密码')); // [Geetest] 添加新的验证规则,检查核心参数是否存在 $validator->addRule('lot_number', 'required', _t('请完成人机验证')); /** 截获验证异常 */ // [Geetest] 将 lot_number 加入验证列表 if ($error = $validator->run($this->request->from('name', 'password', 'lot_number'))) { // ... (这里的代码保持不变) } /** [Geetest] 在验证用户名密码前,执行二次验证 */ $geetest_result = $this->_geetestValidate( $this->request->get('lot_number'), $this->request->get('captcha_output'), $this->request->get('pass_token'), $this->request->get('gen_time') ); if (!$geetest_result || $geetest_result['result'] !== 'success') { /** 设置提示信息 */ $reason = isset($geetest_result['reason']) ? $geetest_result['reason'] : 'Unknown Error'; $this->widget('Widget_Notice')->set(_t('人机验证失败: ') . $reason); // 或 Notice::alloc()->set() $this->response->goBack(); } /** 开始验证用户 (原有的登录逻辑) **/ // ... (后续的 $this->user->login() 等代码保持不变) }
步骤二:修改前台登录模板 (admin/login.php
)
现在我们需要在前端页面上显示验证码并配合后端的逻辑。
- 打开
admin/login.php
文件。 在表单中,于“密码”输入框和“登录”按钮之间,添加一个用于承载验证码的容器。
<!-- ... 密码输入框 ... --> <p> <label for="password" class="sr-only"><?php _e('密码'); ?></label> <input type="password" id="password" name="password" ... /> </p> <!-- [Geetest] 验证码容器 --> <div id="captcha-container" style="margin-bottom: 1.2rem;"></div> <!-- [优化] Banner 提示将会被动态插入到这里 --> <p class="submit"> <button type="submit" ...><?php _e('登录'); ?></button> ... </p> <!-- ... -->
在文件底部
<?php include 'footer.php'; ?>
之前,添加 Geetest 的 JS 库引用和初始化脚本。> **最佳实践**:为了提供更好的用户体验,我们使用内联的 Banner 提示代替 `alert()` 弹窗。 首先,在 `admin/login.php` 文件顶部添加 Banner 的 CSS 样式。
<style> .geetest-banner { background-color: #f8d7da; color: #721c24; padding: 10px 15px; margin-bottom: 1.2rem; border-radius: .25rem; text-align: center; } </style>
然后,添加以下 JavaScript 代码。
<!-- [Geetest] 引入 Geetest JS 并添加初始化脚本 --> <script src="https://static.geetest.com/v4/gt4.js"></script> <script> document.addEventListener('DOMContentLoaded', function () { // --- 请在这里填入你的 Geetest CAPTCHA_ID --- var geetestCaptchaId = '你的_CAPTCHA_ID'; // ------------------------------------ var form = document.querySelector('form[name="login"]'); var captchaInstance = null; // Banner 提示函数 function showBannerMessage(message) { removeBannerMessage(); var banner = document.createElement('div'); banner.id = 'geetest-banner'; banner.className = 'geetest-banner'; banner.textContent = message; form.insertBefore(banner, form.querySelector('.submit')); } function removeBannerMessage() { var banner = document.getElementById('geetest-banner'); if (banner) banner.remove(); } initGeetest4({ captchaId: geetestCaptchaId }, function (captcha) { captchaInstance = captcha; captcha.appendTo('#captcha-container'); }); form.addEventListener('submit', function (e) { removeBannerMessage(); if (!captchaInstance) { showBannerMessage('验证码加载中,请稍候。'); e.preventDefault(); return; } var result = captchaInstance.getValidate(); if (!result) { showBannerMessage('请先完成人机验证!'); e.preventDefault(); } else { Object.keys(result).forEach(function(key) { if (!form.querySelector('input[name="' + key + '"]')) { var input = document.createElement('input'); input.type = 'hidden'; input.name = key; input.value = result[key]; form.appendChild(input); } }); } }); }); </script>
扩展至注册页面
将 Geetest 应用于注册页面的方法与登录页完全相同,只需重复以上步骤,但操作于对应的注册文件。
- 后端文件:
var/Widget/Register.php
- 前台文件:
admin/register.php
操作要点:
- 在
Widget_Register.php
中,同样添加_geetestValidate()
和_sendPost()
方法。 - 在
Widget_Register::action()
方法中,参照Widget_Login::action()
的修改方式,在执行用户数据插入数据库之前,加入 Geetest 的二次验证逻辑。 - 在
admin/register.php
中,同样添加验证码容器<div>
和底部的 JavaScript 初始化脚本。
总结
通过遵循本教程,将 Geetest 4.0 以一种安全、稳定且符合 Typecho 框架设计的方式集成到了您的网站中。这种深度集成的方法不仅提升了安全性,还保证了代码的可维护性。当 Typecho 版本更新时,只需关注 Widget
和模板文件的相应变化,即可轻松地迁移您的安全防护功能。
评论区
还没有人评论