# Captcha **Repository Path**: houc106/captcha ## Basic Information - **Project Name**: Captcha - **Description**: 本系统是一款基于 Spring Boot 与 React 的图形验证码组件库,以弹框形式集成于业务系统。支持滑动拼图、文字点选、图形点选、VTT空间语义、滑块进度、自由变形、符号顺序点选共 7 种验证玩法,通过 bizCode 实现多业务差异化配置。后端以JAR包方式集成到业务系统,无需独立部署;前端提供封装控件,一键接入,覆盖 PC 与移动端浏览器场景。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2026-08-11 - **Last Updated**: 2026-08-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 图形验证码 图形验证码 Starter(Spring Boot Starter + React Web/H5 前端),用于以弹框形式接入业务系统。当前支持按后端 YAML 配置切换不同验证码玩法,也支持多个业务系统通过 `bizCode` 使用不同方案。 当前只支持 Web/H5,也就是 PC 浏览器和手机浏览器;暂不提供原生 App、小程序或桌面端专用 SDK。 ## 模块 - `captcha-spring-boot-starter`:验证码自动配置 starter(生成器 / 服务 / 控制器) - `captcha-demo`:Spring Boot 示例应用(端口 `18090`) - `frontend`:React + Vite 前端 demo(端口 `5176`) ## 验证码类型 | 业务编码 | mode 可选值 | 中文表述 | 玩法 | | --- | --- | --- | --- | | `A` | `slide_puzzle` | 滑动拼图验证 | 拖动拼图块,让拼图块与背景缺口重合 | | `B` | `text_click` | 文字点选验证 | 按提示给出的文字顺序,依次点击图片中的文字 | | `C` | `image_click` | 图形点选验证 | 按提示给出的图形顺序,依次点击对应图形 | | `D` | `vtt_semantic` | VTT 空间语义验证 | 根据空间关系提示,点击目标物体周围指定方位的物体 | | `E` | `progressive` | 滑块进度驱动验证 | 向右拖动滑块,驱动前景曲线逐步贴合背景目标曲线 | | `F` | `freeform` | 自由变形验证 | 拖动白色曲线控制点,让整条曲线与目标曲线重合 | | `G` | `symbol_click` | 符号顺序点选验证 | 按提示顺序,在大图中依次点击数字、图标或表情符号 | 所有模式的弹框标题统一配置为 `安全验证`。 ## 接入方式 后端统一暴露验证码接口,默认路径为 `/captcha`: - `GET /captcha?bizCode=A`:生成验证码 - `POST /captcha/verify`:校验验证码 业务系统推荐以弹框形式集成前端组件: ```tsx import { CaptchaModal } from 'captcha-frontend' setOpen(false)} apiBase="/captcha" bizCode="A" onSuccess={(token) => console.log('success', token)} /> ``` 如果需要在自定义弹层里内嵌验证码,也可以直接使用 `ArcSliderCaptcha`。 demo 页面也支持通过 URL 查询参数快速切换: ```text http://localhost:5176/?bizCode=A http://localhost:5176/?bizCode=B http://localhost:5176/?bizCode=C http://localhost:5176/?bizCode=D http://localhost:5176/?bizCode=E http://localhost:5176/?bizCode=F http://localhost:5176/?bizCode=G ``` ## Demo 说明 `captcha-demo` 和 `frontend` 主要是给业务系统做集成参考,不是另一个独立产品。 demo 里已经把完整链路串起来了: - 前端 `frontend/src/main.tsx` 通过 `bizCode` 决定加载哪种验证码 - 验证码组件统一是 `ArcSliderCaptcha` - 后端接口统一走 `/captcha` 和 `/captcha/verify` - 业务编码和验证码类型由 `captcha-demo/src/main/resources/application.yml` 配置 业务系统集成时,可以直接照着 demo 的方式做: ```tsx const bizCode = new URLSearchParams(window.location.search).get('bizCode') || 'F'; ``` 如果你是在业务系统里真实接入,一般不需要暴露 `bizCode` 给用户自己选,而是由你们自己的业务页面决定传什么值。demo 里把它放到 URL 上,只是为了方便调试 A-G 各种方案。 ### 业务系统集成与生产部署提示 > **重要:生产环境默认将 `captcha-spring-boot-starter` 集成到业务系统后端的 jar 中,不需要单独启动 captcha 服务。** 业务系统上线前请确认以下内容: 1. **默认集成方式:随业务系统一起打包** - 业务系统后端通过 Maven 引入 `captcha-spring-boot-starter`。 - 业务系统重新打包后,验证码 Controller、生成器、校验逻辑和背景图片资源都会随业务系统 jar 一起进入运行包。 - 业务系统启动后,直接通过自身域名和端口提供 `/captcha`、`/captcha/verify` 接口。 - 不需要额外启动 `captcha-demo`,也不需要额外占用 `18090` 端口。 2. **业务系统后端依赖示例** ```xml com.houc.captcha captcha-spring-boot-starter 1.0.4 ``` 引入依赖后,在业务系统的 `application.yml` 中配置 `captcha.*`,然后按业务系统原有方式打包和部署即可。 3. **确认背景图片资源已部署** - 当前 starter 默认会从 starter jar 内的 `resources/backgrounds/` 加载背景资源;这些资源会随 Maven 依赖进入业务系统的最终 jar。 - 生产打包时不能排除 starter 依赖中的 `backgrounds` 资源目录。 - 如果后续改为外部图片目录,或业务系统自行配置了 `image-path`,则必须将该目录同步到生产服务器,并确认运行账号有读取权限。 - 背景资源缺失时,部分方案可能只能回退到默认渐变背景,或导致验证码生成异常。 4. **确认前端接口地址** - 本地 demo 通过 Vite 代理将 `/captcha` 转发到独立运行的 demo 后端 `http://localhost:18090`。 - 业务系统集成后,`apiBase` 通常使用业务系统自己的相对路径 `/captcha`,不需要指向 `18090`。 - 如果业务前端和业务后端跨域部署,再根据实际架构配置 CORS、HTTPS 和网关转发。 `captcha-demo` 只是一个可运行示例,使用 `18090` 是为了本地演示方便;它不是生产环境必须部署的 captcha 服务。 上线前至少验证: ```text 业务系统 GET /captcha?bizCode=A 能正常返回验证码 业务系统 POST /captcha/verify 能正常返回校验结果 业务系统 jar 已包含 captcha starter 依赖 背景图片资源 已打包且可读取 ``` ## 后端配置 如果只需要全局一种验证码,配置 `captcha.mode` 即可: ```yaml captcha: mode: slide_puzzle ``` 如果多个业务系统共用同一套验证码服务,推荐按 `bizCode` 独立配置。A 系统传 `bizCode=A`,B 系统传 `bizCode=B`,后端会按业务编码返回对应模式;修改某个业务编码的配置,只影响传入该 `bizCode` 的业务系统。 如果前端传了未配置的 `bizCode`,例如 `H`,当前实现会自动回落到全局 `captcha.mode` 作为兜底方案;如果 `captcha.mode` 也没有特别配置,则默认使用 `freeform`。也就是说,未命中的业务编码不会报错,而是拿到默认验证码。 如果你希望兜底更明确,也可以单独约定一个默认业务编码,例如把公共兜底业务配置为 `DEFAULT`,然后前端没有命中时统一传这个值。 ```yaml captcha: enabled: true mode: freeform businesses: A: mode: slide_puzzle title: 安全验证 prompt: 拖动拼图块使其与缺口重合 B: mode: text_click title: 安全验证 C: mode: image_click title: 安全验证 D: mode: vtt_semantic title: 安全验证 E: mode: progressive title: 安全验证 prompt: 向右滑动完成曲线与背景匹配 F: mode: freeform title: 安全验证 prompt: 拖动白线到与目标线完全重合 G: mode: symbol_click title: 安全验证 ``` ## 玩法说明 ### A. 滑动拼图验证 页面展示一张背景图、一个缺口和一个可拖动拼图块。用户横向拖动拼图块,直到拼图块与缺口位置重合后松手,后端根据提交的拼图坐标和容差判断是否通过。

滑动拼图验证示例 1 滑动拼图验证示例 2 滑动拼图验证示例 3

### B. 文字点选验证 页面会在图片中随机放置多个文字,并在下方给出需要点击的文字顺序。用户必须按提示顺序依次点击对应文字,点错、顺序错误或点击位置超出容差都会验证失败。

文字点选验证示例 1 文字点选验证示例 2 文字点选验证示例 3

### C. 图形点选验证 页面会展示多个不同颜色和形状的图形,并在提示中给出目标图形及点击顺序。用户需要按照提示依次点击指定图形,后端根据点击顺序和热点坐标容差完成校验。

图形点选验证示例 1 图形点选验证示例 2 图形点选验证示例 3

### D. VTT 空间语义验证 页面会展示多个物体,提示语会指定一个参照物和空间关系,例如“字母 G 正上方的物体”“某物体左边的物体”“某物体下方的物体”。用户需要理解画面中的相对位置关系并点击目标物体,后端校验点击热点。

VTT 空间语义验证示例 1 VTT 空间语义验证示例 2 VTT 空间语义验证示例 3

### E. 滑块进度驱动验证 页面展示背景目标曲线和前景曲线。用户向右拖动底部滑块,滑块进度会驱动前景曲线发生变化;当前景曲线与背景目标曲线匹配时松手,后端根据曲线采样误差判断是否通过。

滑块进度驱动验证示例 1 滑块进度驱动验证示例 2 滑块进度驱动验证示例 3

### F. 自由变形验证 页面展示背景目标曲线和可拖动的白色曲线。用户拖动白色曲线上的控制点,让前景曲线尽可能与目标曲线重合;后端会按整条曲线采样,综合平均误差、最大误差和端点误差判断是否通过。

自由变形验证示例 1 自由变形验证示例 2 自由变形验证示例 3

### G. 符号顺序点选验证 页面下方展示“请依次点击”的符号序列,符号可能是数字、简笔图标或表情。用户需要在大图背景中按顺序点选对应符号,点满后自动提交;后端根据点击顺序和热点坐标容差完成校验。

符号顺序点选验证示例 1 符号顺序点选验证示例 2 符号顺序点选验证示例 3

## 运行 ```bash # 后端 mvn -pl captcha-demo -am spring-boot:run # 前端 cd frontend npm install npm run dev ``` ## 发布约定 - 每次修改后端 `captcha-spring-boot-starter` 相关代码后,都递增一个小版本号。 - 例如当前是 `1.0.4`,下一次后端变更发布时应升级为 `1.0.5`。 - 发布后将新版本上传到 Maven 仓库,业务系统只需把依赖版本改成最新小版本并重启即可。 - 前端 demo 的版本号不影响业务系统接入,仅后端 starter 版本需要跟着发布。 ## 本地自测 启动后端(默认端口 `18090`)与前端(默认端口 `5176`)后,即可在浏览器中自测。 ### 浏览器打开 前端 `main.tsx` 通过 URL 的 `bizCode` 参数决定加载哪种验证码,因此直接用查询参数打开对应页面即可: ```text http://localhost:5176/?bizCode=A 滑动拼图验证 http://localhost:5176/?bizCode=B 文字点选验证 http://localhost:5176/?bizCode=C 图形点选验证 http://localhost:5176/?bizCode=D VTT 空间语义验证 http://localhost:5176/?bizCode=E 滑块进度驱动验证 http://localhost:5176/?bizCode=F 自由变形验证 http://localhost:5176/?bizCode=G 符号顺序点选验证 ``` `/captcha` 接口已通过 Vite 代理转发到后端 `http://localhost:18090`,无需处理跨域。 ### 自动化验收 本次拆分后,后端自动化测试覆盖了七种验证码模式: - A-G 业务编码能路由到各自配置的 `mode`。 - 七种模式都能生成 `token`、背景图和正确的客户端展示字段。 - 自由变形、进度曲线、滑动拼图、文字点选、图形点选、VTT 空间语义、符号点选都能用正确答案验证通过。 - 错误答案和其他模式的请求字段会验证失败。 - 目标曲线、目标进度、拼图缺口坐标、点选热点等敏感答案不会下发给前端。 - 验证通过后的 token 只能被业务系统消费一次。 本地完整验证命令: ```bash /Users/houc/Documents/develop/apache-maven-3.9.11/bin/mvn \ -s /Users/houc/Documents/develop/apache-maven-3.9.11/conf/settings.xml \ -Dmaven.repo.local=/Users/houc/Documents/develop/apache-maven-3.9.11/my-repository \ -pl captcha-spring-boot-starter -am test cd frontend npm run build ``` ### 拆分后的代码边界 后端公共 Service 只负责令牌生命周期、`bizCode` 路由、过期清理、失败次数和验证通过 token 的一次性消费。每种验证码的生成、服务端答案和校验逻辑在自己的策略中: - `FreeformCaptchaStrategy` - `ProgressiveCaptchaStrategy` - `SlidePuzzleCaptchaStrategy` - `TextClickCaptchaStrategy` - `ImageClickCaptchaStrategy` - `VttSemanticCaptchaStrategy` - `SymbolClickCaptchaStrategy` 点选类共用绘制和布局基类 `AbstractSelectionCaptchaGenerator`,但文字点选、图形点选、VTT 空间语义、符号点选分别有自己的 Generator。前端 `ArcSliderCaptcha` 只负责加载、刷新和按 `mode` 路由;具体交互分别在 `FreeformCaptcha.tsx`、`ProgressiveCaptcha.tsx`、`SlidePuzzleCaptcha.tsx`、`SelectionCaptcha.tsx`、`SymbolClickCaptcha.tsx` 中。 ### 方案 A 自测步骤(滑动拼图) 1. 浏览器打开:**`http://localhost:5176/?bizCode=A`** 2. 页面加载出一张背景图、一个缺口和一个可拖动的拼图块(响应 `mode` 为 `slide_puzzle`)。 3. 横向拖动拼图块,使其与背景缺口位置重合后松手,前端自动提交 `POST /captcha/verify`。 4. 校验通过会回调 `onSuccess(token)`;位置偏差超过容差则失败,可刷新重试。 自测时可顺手验证: - **正常通过**:拖到缺口松手 → 校验成功。 - **错误位置**:拖到错误位置松手 → 校验失败且可重试。 - **失败锁定**:连续校验失败达到上限(默认 5 次,`captcha.maxVerifyAttempts`)后该 `token` 作废,需刷新获取新验证码。 - **答案不泄露**:浏览器 Network 中查看 `GET /captcha?bizCode=A` 的响应,其中不含 `holeX` / `holeY`(正确缺口坐标仅在服务端内存,不回传前端)。 若只验证接口、不打开前端,也可直接用 curl: ```bash # 1) 生成验证码,拿到 token curl -s "http://localhost:5176/captcha?bizCode=A" -o /tmp/genA.json TOKEN=$(grep -o '"token":"[^"]*"' /tmp/genA.json | head -1 | sed 's/"token":"//;s/"//') # 2) 提交校验(puzzleX/puzzleY 为拖动终点坐标,需在容差内才通过) curl -s -X POST "http://localhost:5176/captcha/verify" \ -H "Content-Type: application/json" \ -d "{\"token\":\"$TOKEN\",\"bizCode\":\"A\",\"puzzleX\":120,\"puzzleY\":90}" ``` ## 多远程仓库同步 本项目同时托管在两个远程仓库,提交时需保持两边一致: - `origin`(主) → 阿里云 CodeUp:`https://codeup.aliyun.com/fle-code/operation/captcha.git` - `gitee` → Gitee:`https://gitee.com/houc106/captcha.git` 两个远程**分开推送**(`origin` 只推阿里云,`gitee` 只推 Gitee),避免双推时命令输出掩盖真实状态: ```bash # 提交本地改动 git add -A git commit -m "your message" # 分别推送到两个仓库 git push origin master # 只推阿里云 CodeUp git push gitee master # 只推 Gitee ``` > 经验:之前曾用 `origin` 同时配置两个 push 地址做"一条命令双推",但当本地已领先阿里云时, > `git push origin master` 会误报 `Everything up-to-date`(假象)。改为分开推送后,每个命令结果都真实可靠。 ### 先拉后推(避免推送被拒) 如果任一方(如 Gitee 网页端)有独立改动,需先合并再推送: ```bash # 拉取 Gitee 的独立改动并合并 git fetch gitee git merge gitee/master # 分别推送 git push origin master git push gitee master ``` ### 用真实远端状态核对(排查假象) `git push` 的 `Everything up-to-date` 不一定可信(可能引用错位)。不确定时,直接查真实远端: ```bash git ls-remote origin master # 阿里云真实 master 提交 git ls-remote gitee master # Gitee 真实 master 提交 git rev-parse HEAD # 本地当前提交 ``` 三者应一致。若本地领先某远程,用单 URL 直推该远程最稳妥: ```bash git push https://codeup.aliyun.com/fle-code/operation/captcha.git master ``` ### 常用组合 | 命令 | 作用 | | --- | --- | | `git push origin master` | 推送到阿里云 CodeUp | | `git push gitee master` | 推送到 Gitee | | `git fetch gitee && git merge gitee/master` | 合并 Gitee 的独立改动 | | `git fetch origin` | 拉取阿里云(fetch) | | `git ls-remote origin master` | 查阿里云真实远端提交 |