☰
TaleBook 人机验证功能实现指南:基于极验 GeeTest 的可插拔验证架构
2026/10/5 6:44:33 网站建设 项目流程
  • 后端
  • 前端
  • CMS

【免费下载链接】talebook

一个简单好用的个人书库

项目地址:https://gitcode.com/gh_mirrors/ta/talebook
点击查看免费下载

导读

本文完整解析 TaleBook(个人书库)中人机验证功能的落地实现:它以极验 GeeTest v4 为主要提供商,通过"抽象基类 + 动态加载器"设计预留了 reCAPTCHA、hCaptcha 等扩展接口,并在此基础上额外实现了无需外部 API 的内置图形验证码。读者将掌握验证配置项的作用与取值范围、/api/captcha/config等三个接口的契约、极验二次验证的 HMAC-SHA256 签名细节,以及验证能力如何被注册、登录、私人图书馆访问三个场景复用。

功能需求总览

根据实现计划文档(人机验证功能实现计划),人机验证功能的定位是:为 TaleBook 添加行为验证能力,支持极验(GeeTest),并预留扩展接口以便后期接入其他验证方式。

需求拆分为四个维度:

维度内容
设置页配置项验证方式下拉选择(提供商)、三个场景启用复选框(注册 / 登录 / 私人图书馆)、API 密钥(Captcha ID 公钥、Captcha Key 私钥)
前端组件通用验证组件CaptchaWidget.vue,支持按配置动态加载不同提供商的 SDK
后端模块webserver/plugins/captcha/目录,内含模块加载器、验证基类、极验实现
API 接口/api/captcha/config(前端初始化配置)、/api/captcha/verify(二次验证),并改造登录 / 注册 / 欢迎接口增加验证参数校验

从仓库实际代码看,落地实现还额外增加了图形验证码提供商(image)与重置密码场景(reset),比原始计划文档更进一步——这意味着"预留扩展接口"的设计目标已经产生了第一个实际扩展。

可插拔架构:基类、加载器与提供商

抽象基类BaseCaptchaProvider

webserver/plugins/captcha/base.py 定义了所有验证提供商的统一契约,使用abc.ABC与abstractmethod:

  • name:提供商名称标识(如geetest);
  • sdk_url:前端 SDK 的加载地址;
  • is_configured():检查该提供商是否已完成密钥等配置;
  • get_frontend_config():返回前端初始化所需的配置字典(provider、captchaId、sdkUrl);
  • verify(**kwargs):执行二次验证,不同提供商通过 kwargs 接收各自参数;
  • is_enabled_for(scene):场景开关检查,实现为self.settings.get(f"CAPTCHA_ENABLE_FOR_{scene.upper()}", False),支持register、login、welcome、reset四种场景。

任何新验证方式(如 reCAPTCHA、hCaptcha)只需继承该基类实现四个抽象方法,即可无缝接入。

动态加载器__init__.py

webserver/plugins/captcha/init.py 承担提供商注册与实例管理:

_CAPTCHA_PROVIDERS = { "image": ImageCaptchaProvider, "geetest": GeetestProvider, }

对外暴露四个统一函数:

  • get_available_providers():返回提供商名与显示名的映射;
  • get_captcha_provider(settings):读取CAPTCHA_PROVIDER配置创建/缓存实例(含全局缓存,避免每次请求重建);
  • is_captcha_enabled(settings, scene):判断某场景是否启用验证(提供商未配置则直接返回 False);
  • verify_captcha(settings, **kwargs):统一执行验证,未配置提供商时默认放行(返回 True);
  • get_captcha_config(settings):聚合提供商前端配置与四个场景开关,组装成前端初始化配置;未启用或提供商未配置完整时返回None。

该加载器的get_captcha_config最终返回形如下方的结构,供前端一次性获取"要不要显示验证、显示哪家、哪个场景开":

{ "provider": "geetest", "captchaId": "xxx", "sdkUrl": "https://static.geetest.com/v4/gt4.js", "enabled": true, "scenes": { "register": true, "login": false, "welcome": false, "reset": false } }

配置项详解

所有配置项定义于 webserver/settings.py,并已加入后端设置白名单(见下文"设置页面集成"):

'CAPTCHA_PROVIDER': '', # 验证提供商,可选值: 'geetest'、'image' 或空字符串表示不启用 'CAPTCHA_ENABLE_FOR_REGISTER': False, # 注册界面启用认证 'CAPTCHA_ENABLE_FOR_LOGIN': False, # 登录页面启用认证 'CAPTCHA_ENABLE_FOR_WELCOME': False, # 私人图书馆界面启用认证 'CAPTCHA_ENABLE_FOR_RESET': False, # 重置密码页面启用认证 'GEETEST_CAPTCHA_ID': '', # 极验 Captcha ID (公钥) 'GEETEST_CAPTCHA_KEY': '', # 极验 Captcha Key (私钥)

此外,image_captcha.py 从 settings 读取三个可调参数(均有默认值,非必须配置):

  • IMAGE_CAPTCHA_LENGTH:验证码字符数,默认 4;
  • IMAGE_CAPTCHA_WIDTH:图片宽度,默认 120 像素;
  • IMAGE_CAPTCHA_HEIGHT:图片高度,默认 40 像素。

API 接口契约

路由定义在 webserver/handlers/captcha.py 的routes()中,共三个接口:

接口方法用途
/api/captcha/configGET返回{"err": "ok", "config": {...}},config 为加载器聚合的前端配置;未启用时 config 为 null
/api/captcha/imageGET生成图形验证码,返回{err, captcha_id, image}(image 为data:image/png;base64,...),正确答案写入安全 Cookie
/api/captcha/verifyPOST二次验证。图形验证码传provider=image&captcha_code=xxx;极验传lot_number、captcha_output、pass_token、gen_time四参数

关键实现细节(CaptchaConfigHandler / CaptchaImageHandler / CaptchaVerifyHandler):

  • 三个 Handler 均继承CaptchaBaseHandler,覆写should_be_invited()放行——验证码接口本身不需要邀请码验证;
  • 图形验证码答案通过set_secure_cookie写入captcha_answer与captcha_generate_time两个 Cookie,2 分钟过期;验证时先做时间检查(超过 120 秒即判定过期并清除 Cookie),再比对答案;
  • 验证通过后保留Cookie(供后续表单提交时复用校验),失败不清理(允许用户重试)。

极验 GeeTest v4 验证流程

完整调用链

依据 geetest.py 与前端 CaptchaWidget.vue 的实现,极验 v4 的全流程为:

  1. 前端加载 SDK:动态注入<script src="https://static.geetest.com/v4/gt4.js">,等待window.initGeetest4就绪;
  2. 初始化:以popup弹窗模式调用window.initGeetest4({ captchaId, product: 'popup', language: 'zho' }, callback);
  3. 用户完成验证:通过gt.getValidate()取得lot_number、captcha_output、pass_token、gen_time四个字段,随表单一并提交到登录 / 注册 / 欢迎接口;
  4. 后端签名:使用私钥对lot_number做 HMAC-SHA256 得到sign_token:
sign_token = hmac.new( captcha_key.encode(), # 私钥作为 HMAC key lot_number.encode(), # lot_number 作为消息 digestmod="SHA256" ).hexdigest()
  1. 调用极验服务端:POST http://gcaptcha4.geetest.com/validate?captcha_id={captcha_id},请求体携带lot_number、captcha_output、pass_token、gen_time、sign_token,超时 10 秒;
  2. 判定:响应 JSON 的result == "success"视为通过,否则记录reason并返回失败。

前端提交细节

以 login.vue 为例,前端在配置启用且scenes.login为真时展示验证组件,收到verify事件后将验证参数按提供商类型拼入表单(signup.vue 逻辑一致):

  • 图形验证码:data.append('captcha_code', ...);
  • 极验:依次追加lot_number、captcha_output、pass_token、gen_time。

验证失败(rsp.err === 'captcha.invalid')时,页面会调用captchaRef.reset()重置验证并要求重新完成。

内置图形验证码:无需外部服务的降级方案

这是仓库相对实现计划文档的新增亮点。 image_captcha.py 基于 PIL 在服务端生成验证码图片,不依赖任何外部 API:

  • 字符集:大写字母 + 数字,剔除易混淆字符0/O/1/I/L;
  • 渲染:随机浅色背景、5 条随机干扰线、字符随机颜色 / 位移 / ±15° 旋转、并按面积 1/10 密度撒噪点;
  • 字体:优先 DejaVuSans-Bold 系统字体,回退arial.ttf,最终回退 PIL 默认字体;
  • 存储与验证:图片以 base64 返回前端,正确答案存入安全 Cookie(2 分钟过期),用户输入统一upper().strip()后比对。

前端对应组件为 ImageCaptchaWidget.vue:加载图片 → 用户输入 → 点击"确认"调用/api/captcha/verify(provider=image)→ 成功后把captcha_code随表单提交;点击图片可刷新验证码。CaptchaWidget.vue根据config.provider === 'image'自动切换到该组件,无需改动宿主页面。

后端业务接口接入:check_captcha 统一校验

webserver/handlers/user.py 提供统一校验函数check_captcha(handler, scene),四个业务接口分别挂接:

接口场景位置
注册(SignUp.post)registeruser.py#L203
登录(SignIn.post)loginuser.py#L272
重置密码(UserReset.post)resetuser.py#L306
私人图书馆访问(Welcome.post)welcomeuser.py#L551

check_captcha的执行策略:

  1. 先调用is_captcha_enabled(CONF, scene),未启用该场景则直接放行;
  2. 存在captcha_code参数 → 走图形验证码分支:校验 Cookie 是否存在、是否超 120 秒过期,比对答案,验证通过后立即清除 Cookie 防止重放;
  3. 否则走极验分支:四个参数缺一即拒绝(返回"请完成人机验证"),全部齐全才调用verify_captcha执行二次验证。

各接口在验证不通过时统一返回{"err": "captcha.invalid", "msg": ...},前端据此显示错误并重置验证码。

设置页面集成

后端在 admin.py 的AdminSettings.KEYS白名单中加入了 7 个验证配置键(CAPTCHA_PROVIDER、四个场景开关、GEETEST_CAPTCHA_ID/KEY),使其可被保存与读取。

前端 GeneralSettingsContent.vue 渲染"人机验证设置"卡片:

  • 验证提供商下拉框:选项来自captchaProviders数组——不启用('')、图形验证码 (无需配置)(image)、GeeTest (极验)(geetest);
  • 四个场景复选框(注册 / 登录 / 私人图书馆 / 重置密码):仅当提供商为image或geetest时显示;
  • 密钥输入框:GEETEST_CAPTCHA_ID与GEETEST_CAPTCHA_KEY,仅当CAPTCHA_PROVIDER === 'geetest'时通过show_when条件显示,Key 使用密码输入框类型。

该卡片归入"访问(access)"分组,与用户设置、社交登录并列(GeneralSettingsContent.vue#L805)。

关键修复:路由注册顺序

实现计划文档记录的"重要修复"在代码中得到确认。 handlers/init.py 中路由按以下顺序拼接:

routes += admin.routes() # ... 各业务路由 routes += captcha.routes() # 人机验证路由(提前) routes += theme.routes() # 主题路由(静态 catch-all 之前) routes += webdav.routes() # WebDAV(静态 catch-all 之前) routes += files.routes() # 最后注册 files,含通配符 r"/(.*)"

由于files.py存在通配符路由r"/(.*)",若 captcha 路由在其之后注册将永远不会被匹配——修复方式是把captcha.routes()移到files.routes()之前,并在启动时打印CAPTCHA routes registered: ['/api/captcha/config', '/api/captcha/image', '/api/captcha/verify']日志。同理由 handlers/captcha.py 启动时输出CAPTCHA disabled或CAPTCHA enabled with provider: geetest及各启用场景,便于运维排障。

翻译文件

中英文翻译键位于 app/i18n/locales/zh-CN.json 与 app/i18n/locales/en-US.json,覆盖设置页标签与验证组件文案:

  • 设置页:captchaSettings(人机验证设置 / CAPTCHA Settings)、captchaProvider、captchaEnableForRegister/Login/Welcome/Reset、geetestCaptchaId/Key、option.none/image/geetest、message.captchaInfo;
  • 组件:captcha.title(人机验证)、loading、verifyFailed、pleaseComplete、loadFailed、inputCode、inputPlaceholder、refresh。

测试覆盖

webserver/../tests/test_captcha.py 以 unittest 提供 462 行测试,核心用例:

  • TestBaseCaptchaProvider:四种场景开关的启用/禁用判定;
  • TestGeetestProvider:is_configured对 ID/Key 缺失的判定、get_frontend_config字段、验证成功(mock 200 +result=success)、验证失败、API 返回 500 时默认通过、网络异常时默认通过、缺少参数时拒绝、未配置时拒绝;
  • TestCaptchaModule:提供商注册表、实例缓存逻辑、未配置时is_captcha_enabled返回 False、verify_captcha未配置时默认放行、get_captcha_config的 null/成功分支。

测试通过MockCaptchaProvider验证了"新增提供商只需继承基类"的扩展路径,与加载器设计互相印证。

管理员配置与安全设计

操作步骤

  1. 进入系统设置 → 人机验证设置;
  2. 选择验证提供商:图形验证码 (无需配置)或GeeTest (极验);
  3. 勾选需要启用验证的场景(注册 / 登录 / 私人图书馆 / 重置密码);
  4. 若选择 GeeTest,在极验官网注册账号并创建应用,获取Captcha ID(公钥)与Captcha Key(私钥)填入对应字段(仅在 geetest 提供商下显示);
  5. 保存配置,重启后端使启动日志生效(CAPTCHA enabled with provider: geetest)。

安全性考虑

  • 验证失败重置:参数校验失败或验证不通过时,前端调用reset()重置验证码强制重新验证;
  • 防重放:图形验证码答案存入安全 Cookie 且 2 分钟过期,业务接口验证通过后即清除;极验gen_time随表单回传参与校验;
  • 业务连续性兜底:极验 API 请求异常(非 200、网络错误、JSON 解析失败)时默认放行,避免第三方服务故障阻塞用户登录/注册——代码注释明确提示生产环境可按需调整该策略;
  • 统一配置管理:所有验证配置(提供商、场景开关、密钥)均经后端白名单持久化,前端仅消费/api/captcha/config的只读配置,不接触私钥。

扩展新验证提供商的方法

遵循现有扩展点,接入新提供商(如 reCAPTCHA、hCaptcha)只需三步:

  1. 在webserver/plugins/captcha/下新建模块,继承BaseCaptchaProvider,实现is_configured、get_frontend_config、verify;
  2. 在init.py 的_CAPTCHA_PROVIDERS注册表中登记,并在get_available_providers()补充显示名;
  3. 前端在CaptchaWidget.vue中按新 provider 分支渲染对应组件,后端check_captcha依据参数形态自动分流(有captcha_code走图形验证码,否则走通用四参数逻辑)。

由此,"支持极验 + 预留扩展"的设计目标在 TaleBook 中既得到了完整落地,也已被内置图形验证码这一实际扩展验证了其可扩展性。

  • 后端
  • 前端
  • CMS

【免费下载链接】talebook

一个简单好用的个人书库

项目地址:https://gitcode.com/gh_mirrors/ta/talebook
点击查看免费下载

相关推荐

上一篇:ktib 核心功能全解析:镜像构建、扫描与融合的终极工具
下一篇:Wand-Enhancer 使用指南:5分钟为 WeMod 解锁本地增强功能

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询