{"id":25655383,"url":"https://github.com/saobby/saobbycaptcha-v3","last_synced_at":"2026-03-17T08:32:55.959Z","repository":{"id":278916253,"uuid":"933532747","full_name":"Saobby/SaobbyCAPTCHA-V3","owner":"Saobby","description":"汉字点选人机验证，支持Docker一键部署","archived":false,"fork":false,"pushed_at":"2025-08-01T10:36:15.000Z","size":146,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-01T12:45:11.758Z","etag":null,"topics":["captcha"],"latest_commit_sha":null,"homepage":"https://captcha-v3.saobby.com/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Saobby.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-02-16T07:25:34.000Z","updated_at":"2025-08-01T10:32:37.000Z","dependencies_parsed_at":"2025-02-22T14:37:20.758Z","dependency_job_id":"27213828-044b-4c77-8ef8-4539e8471a00","html_url":"https://github.com/Saobby/SaobbyCAPTCHA-V3","commit_stats":null,"previous_names":["saobby/saobbycaptcha-v3"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Saobby/SaobbyCAPTCHA-V3","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Saobby%2FSaobbyCAPTCHA-V3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Saobby%2FSaobbyCAPTCHA-V3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Saobby%2FSaobbyCAPTCHA-V3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Saobby%2FSaobbyCAPTCHA-V3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Saobby","download_url":"https://codeload.github.com/Saobby/SaobbyCAPTCHA-V3/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Saobby%2FSaobbyCAPTCHA-V3/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30619131,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T08:10:05.930Z","status":"ssl_error","status_checked_at":"2026-03-17T08:10:04.972Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["captcha"],"created_at":"2025-02-23T21:28:46.945Z","updated_at":"2026-03-17T08:32:55.953Z","avatar_url":"https://github.com/Saobby.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SaobbyCAPTCHA-V3\n\n一个基于汉字点选的简单易用、可配置的人机验证系统。  \n\n---\n\n## 系统要求\n\n- **操作系统**:\n  \n  - 仅支持 Linux\n\n- **运行环境**：  \n  \n  - Docker\n  - Redis ≥ 6.0  \n\n---\n\n## 预览\n\nhttps://github.com/user-attachments/assets/682f324c-f08e-4ade-834f-b3d34c1d2b94\n\n[在线体验](https://captcha-v3.saobby.com/)  \n\n---\n\n## 功能亮点\n\n- **用户友好**: 在完成一次验证码后，后几次可以不用再完成。\n\n- **预生成验证码**：空闲时自动生成备用图片，应对可能突然出现高并发场景。  \n\n- **速率限制**：拦截高频访问，对可疑用户强制验证。  \n\n- **灵活配置**：支持自定义背景图、字体、验证码有效期等。  \n\n- **轻量集成**：提供前端 JS 脚本与后端验证接口，快速嵌入业务系统。  \n\n---\n\n## 快速开始\n\n### 1. 准备资源文件\n\n```bash\n# 创建背景图、字体和日志目录  \nmkdir -p captcha/{backgrounds,fonts,log}  \n# 创建词语列表\ntouch captcha/words.txt  \n\n# 示例：添加词语（2~5 个汉字）  \necho -e \"验证码\\n网络安全\\n人工智能\" \u003e captcha/words.txt  \n```\n\n- **背景图**：将一些 `.jpg/.png` 文件放入 `captcha/backgrounds`（建议尺寸 300px×225px，不是这个尺寸将会被强制缩放为这个尺寸）。  \n- **字体**：将几个 `.ttf` 文件放入 `captcha/fonts`。  \n- **词语**: 将一些 2~5 个字的词语放入 `captcha/words.txt`。\n  \n  \n  \n  在生成验证码时，会随机选择词语、背景图和字体。\n\n### 2. 使用 Docker 部署\n\n```bash\ndocker run -d \\  \n  -p 8080:5000 \\  \n  -v /path/to/captcha/backgrounds:/app/backgrounds \\  \n  -v /path/to/captcha/fonts:/app/fonts \\  \n  -v /path/to/captcha/log:/app/log \\  \n  -v /path/to/captcha/words.txt:/app/words.txt \\  \n  -e REDIS_HOST=127.0.0.1 \\  \n  -e REDIS_PORT=6379 \\  \n  -e API_VERIFY_TOKEN_PATH=/api/your_secret_verify \\  \n  -e REAL_IP_HEADER= \\  \n  --restart=unless-stopped \\  \n  axolotltech/saobby-captcha-v3:latest  \n```\n\n**安全提示**：  \n\n- 强烈建议通过 Nginx 配置 HTTPS 反向代理，避免 Token 明文传输，还能对响应进行压缩。\n\n- 设置 `API_VERIFY_TOKEN_PATH` 为复杂路径（如 `/api/your_secret_verify`），防止 token 验证接口暴露。  \n\n**状态监控**:\n\n- 使用 `docker ps` 检查容器状态，如果为 `healthy` 则启动成功，如果一直是 `unhealthy`，请查看故障排除章节。\n\n- 如果使用了反向代理，查看 `captcha/log/access.log` 检查入站 IP 以确认 `REAL_IP_HEADER` 是否配置正确。\n\n## 详细配置\n\n### 关键环境变量\n\n| 键                       | 说明                                                                                              | 默认值               |\n| ----------------------- | ----------------------------------------------------------------------------------------------- | ----------------- |\n| `REDIS_HOST`            | Redis 服务地址，可以是 IP 或域名                                                                           | 127.0.0.1         |\n| `REDIS_PORT`            | Redis 端口                                                                                        | 6379              |\n| `API_VERIFY_TOKEN_PATH` | 设置你的网站后端用于校验 token 的 API 的访问路径。由于本接口没有速率限制，应尽量避免外部访问，**因此请设置一个其他人猜不到的路径。**                      | /api/verify_token |\n| `REAL_IP_HEADER`        | 用于获取用户真实 IP 的请求头(比如 `x-forwarded-for`)，留空则使用网络连接中的 IP。**\u003cu\u003e如果使用了 Nginx 做反向代理，请务必正确设置此项！！！\u003c/u\u003e** |                   |\n| `IMAGE_POOL_SIZE`       | 预生成验证码图片池大小（设为 `0` 禁用图片预生成）                                                                     | 500               |\n| `RATE_LIMIT_MAX_AMOUNT` | 10 秒内允许的最大请求次数，超出触发验证码强制验证                                                                      | 10                |\n| `BYPASS_TIMES`          | 成功验证后，后续多少次请求可跳过验证                                                                              | 5                 |\n| `TOKEN_EXPIRATION_TIME` | Token 有效期（单位：秒），建议 ≤ 300                                                                        | 120               |\n\n\u003cdetails\u003e\n  \u003csummary\u003e展开完整环境变量配置\u003c/summary\u003e\n\n  |键|描述|默认值|\n  |---|---|---|\n  |`REDIS_HOST`|Redis 服务地址，可以是 IP 或域名|127.0.0.1|\n  |`REDIS_PORT`|Redis 端口|6379|\n  |`REDIS_USERNAME`|Redis 用户名(如果没有设置请留空)| |\n  |`REDIS_PASSWORD`|Redis 访问密码(如果没有设置请留空)| |\n  |`REDIS_RETRY_ON_TIMEOUT`|Redis 请求超时时，是否重试|true|\n  |`REDIS_MAX_CONNECTIONS`|Redis 连接池的最大连接数|1024|\n  |`REDIS_PREFIX`|程序向 Redis 中存储内容时键的前缀(如果多个本验证码系统依赖同一个 Redis 实例，请设置不同的前缀)|saobby_captcha_|\n  |`REAL_IP_HEADER`|用于获取用户真实 IP 的请求头(比如 `x-forwarded-for`)，留空则使用网络连接中的 IP。**\u003cu\u003e如果使用了 Nginx 做反向代理，请务必正确设置此项！！！\u003c/u\u003e**| |\n  |`CORS_ORIGIN`|设置响应头中 Access-Control-Allow-Origin 的值。如果设置为 * , 则会赋值为请求头中 Origin 的值(老旧浏览器不支持通配符)|*|\n  |`API_VERIFY_TOKEN_PATH`|设置你的网站后端用于校验 token 的 API 的访问路径。由于本接口没有速率限制，应尽量避免外部访问，**因此请设置一个其他人猜不到的路径。**|/api/verify_token|\n  |`ENABLE_TEST_PAGE`|设置是否启用测试页面，启用后，在访问根路径时会显示一个测试页面|true|\n  |`IMAGE_POOL_SIZE`|预生成验证码图片池大小（设为 `0` 禁用图片预生成）|500|\n  |`CHALLENGE_EXPIRATION_TIME`|验证码的完成时限(单位: s)|300|\n  |`TOKEN_EXPIRATION_TIME`|验证码完成后获得的 token 的有效期(单位: s)(不建议设置太长)|120|\n  |`RATE_LIMIT_TIME_INTERVAL`|速率限制的测速区间(单位: s)|10|\n  |`RATE_LIMIT_MAX_AMOUNT`|在 1 个测速区间内，最多访问的次数|10|\n  |`RATE_LIMIT_ENABLE_PENALTY`|达到速率限制时，是否强制每次都需要完成验证码。|true|\n  |`RATE_LIMIT_BAN_DURATION`|达到速率限制的惩罚的有效时间(单位: s)|14400 (4 小时)|\n  |`BYPASS_TIMES`|完成一个验证码后，后面的多少次可以不用再完成|5|\n  |`BYPASS_EXPIRATION_TIME`|完成一个验证码后，后面几次可不用再完成的效果的有效期(单位: s)(不建议设置太长)|300|\n  |`TIP_FONT_SIZE`|提示文本图片的字体大小|30|\n  |`CHALLENGE_FONT_SIZE`|验证码图片中文字的大小|45|\n  |`TIP_MAX_ROTATE_ANGLE`|提示文本图片的字体最大旋转角度|20|\n  |`CHALLENGE_MAX_ROTATE_ANGLE`|验证码图片的字体最大旋转角度|45|\n  |`BACKGROUND_IMAGES_DIR`|容器内存放背景图片的目录，不建议修改，直接修改绑定宿主机的目录即可。|./backgrounds|\n  |`FONTS_DIR`|存放字体的目录，不建议修改，直接修改绑定宿主机的目录即可。|./fonts|\n  |`WORDS_PATH`|存放词汇列表(一行一个)的文本文件(UTF-8编码)，不建议修改，直接修改绑定宿主机的目录即可。|./words.txt|\n  |`DO_INIT`|程序启动时，是否进行初始化操作。初始化操作包括:将所有背景图片缩放到所需要的尺寸，并转换为 jpg 格式(不会重复转换);开始填充图片池; 不建议修改。|true|\n  |`GUNICORN_PORT`|容器内监听的端口。不建议修改，直接修改容器端口映射即可。|5000|\n  |`GUNICORN_LOG_LEVEL`|Gunicorn 日志级别。不建议修改。|warning|\n  |`LOGS_DIR`|容器内访问日志和错误日志的存放目录，不建议修改，直接修改绑定宿主机的目录即可。|./log|\n\u003c/details\u003e\n\n---\n\n## 前端集成\n\n在后端部署完成后，您需要在前端页面中引入验证码的 JavaScript 文件。该文件可以从以下路径获取：\n\n```html\n\u003cscript src=\"https://your-site.com/static/js/saobbyCaptchaV3.js\"\u003e\u003c/script\u003e\n```\n\n请将 `your-site.com` 替换为您实际部署验证码服务的域名。引入此脚本后，您就可以在页面中使用 `SaobbyCaptchaV3` 类来集成人机验证功能。\n\n### 方式 1：表单自动嵌入\n\n```html\n\u003cform method=\"post\" action=\"/login\"\u003e  \n  \u003cinput type=\"text\" name=\"username\"\u003e  \n  \u003cinput type=\"password\" name=\"password\"\u003e  \n  \u003c!-- 验证码容器 --\u003e  \n  \u003cdiv id=\"captcha-container\"\u003e\u003c/div\u003e  \n  \u003cscript\u003e  \n    const captcha = new SaobbyCaptchaV3({  \n      apiBaseUrl: \"https://your-site.com\",  // 建议使用 HTTPS  \n      showTrigger: true,                    // 显示\"点击进行人机验证\"的框\n      container: document.getElementById(\"captcha-container\")  \n    });  \n  \u003c/script\u003e  \n  \u003cbutton type=\"submit\"\u003e登录\u003c/button\u003e  \n\u003c/form\u003e  \n```\n\n这种方式会在网页表单上自动附带上一个 `SCV3_token` 参数，包含人机验证 token，以供后端验证。\n\n### 方式 2：手动触发验证\n\n```javascript\nconst captcha = new SaobbyCaptchaV3({  \n  apiBaseUrl: \"https://your-site.com\",  \n  showTrigger: false  \n});  \n\n// 在需要时调用  \ndocument.getElementById(\"submit-btn\").onclick = async () =\u003e {  \n  const result = await captcha.verify();  \n  if (result.retcode === 0) {  \n    console.log(\"Token:\", result.data.token);  \n  }  \n};  \n```\n\n这种方式更加灵活，但是需要前端手动将 token 发送给后端。\n\n### 详细文档\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开前端接口详细说明\u003c/summary\u003e\n\n  `SaobbyCaptchaV3` 是一个类，包含以下几个方法:  \n  \n  - `constructor(options)` (创建对象)  \n    `options` 是一个 `object`，有以下几个键:\n      |键|数据类型|是否必须|说明|默认值|\n      |---|---|---|---|---|\n      |`apiBaseUrl`|string|是|验证码系统的公网访问地址，比如`https://captcha.your-site.com`(末尾没有`/`)。**强烈建议再使用 nginx 反向代理本验证码服务，以便对响应进行压缩和配置 SSL 证书**|无|\n      |`showTrigger`|bool|否|是否显示\"点击进行人机验证\"的框框|false|\n      |`container`|DOM 元素|否|设置验证码组件要放在哪个元素内(见示例)。如果设置了`showTrigger`为`true`则此项必须指定。|如果不指定会自动创建一个`div`标签用于放置组件|\n      |`once`|bool|否|在验证完一次后，是否销毁人机验证对象。如果设置了`showTrigger`为`true`则不能设置本项为`true`|false|\n  \n  - `verify()`  \n    返回一个 `Promise`。调用时会进行人机验证，并返回结果。  \n    这个 `Promise` 只会 resolve。  \n    返回的结果是一个 `object`，结构如下:  \n    |键|数据类型|说明|示例|\n    |---|---|---|---|\n    |`retcode`|integer|验证结果的类型|`0`: 验证成功; `1`: 用户点了×关闭验证窗口; `429`: 请求过于频繁; `500`: 服务器内部错误; `-1`: 网络错误;|\n    |`msg`|string|错误消息|\"用户取消了验证\"|\n    |`data`|object|验证结果的数据值,如果验证成功,其中会包含验证码 token, 格式为`{token: \"验证码 token\"}`|{token: \"xxxxxxxx\"}|\n  \n  - `destroy()`  \n    手动将验证码的 HTML 从 DOM 中移除，此操作不可逆，移除后无法再使用验证功能，需要重新创建验证码对象。\n\u003c/details\u003e\n\n---\n\n## 后端验证示例（Python）\n\n```python\nimport requests\nimport json\n\ndef verify_token(token: str) -\u003e bool:\n    url = \"https://your-site.com/api/your_secret_verify\"  # 路径与环境变量 API_VERIFY_TOKEN_PATH 一致  \n    response = requests.post(url, \n        headers={\"content-type\": \"application/json\"},   # 必须设置请求头，否则后端会响应 400\n        data=json.dumps({\"token\": token})\n    )\n    response = json.loads(response.text)\n    if response[\"retcode\"]:\n        raise RuntimeError(\"查询失败\")\n    return response[\"data\"][\"result\"]\n```\n\n**注意**：此接口仅能在后端调用，严禁在前端调用，在前端调用是没有防护效果的！\n\n### 详细文档\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开后端接口详细说明\u003c/summary\u003e\n\n  - 验证接口 URL: `/api/verify_token`(可在环境变量中配置)\n \n  - 请求方法: `POST`\n  \n  - 请求头:\n    + `content-type`: `application/json` (**必须**，否则接口会响应 400)\n  \n  - 载荷: 一个 JSON 字符串，包含要验证的 token。格式: `{\"token\": \"xxxxxxxx\"}`\n  \n  - 响应: 一个 JSON 字符串，结构如下:\n    |键|类型|说明|示例|\n    |---|---|---|---|\n    |`retcode`|integer|状态代码|`0`: 成功查询(不代表token有效); `500`: 服务器内部错误;|\n    |`msg`|string|消息|\"OK\"|\n    |`data`|object|结果，格式为`{\"result\": 查询结果}`，查询结果为`true`代表 token 有效，否则 token 无效|{\"result\": true}|\n\u003c/details\u003e\n\n---\n\n## 故障排查\n\n### 常见问题\n\n- **容器状态非 `healthy`**：  \n  检查 `captcha/log/error.log`，常见报错：  \n  \n  - Redis 连接失败 → 检查是否正确设置 Redis 的地址和端口。  \n\n  - 字体/图片加载失败 → 检查是否正确配置目录映射，并放入背景图片和字体\n\n### 漏洞报告\n\n请附带 `error.log` [创建一个 Issue](https://github.com/Saobby/SaobbyCAPTCHA-V3/issues) 来报告问题。\n\n---\n\n## 最佳实践\n\n1. **性能优化**：  \n   - 根据服务器内存调整 `IMAGE_POOL_SIZE`（每张验证码图片约占用 10KB）。  \n\n2. **安全性**：  \n   - 定期更新 `words.txt` 中的词汇。  \n\n---\n\n## 贡献指南\n\n欢迎提交 Issue 或 PR！代码规范：  \n\n- 变量命名：`lower_case_with_underscores`  \n\n- 类命名：`UpperCamelCase`  \n\n- 字符串：统一使用双引号  \n\n- 缩进: 使用 4 个空格\n\n---\n\n## TODO\n\n1. 支持自定义主题色、样式等\n\n---\n\n## 第三方许可证\n\n本系统使用了以下第三方项目的资源，它们的许可证文件存放在仓库 `/third_party_licenses` 目录中：\n\n| 项目名称                                                   | 用途      | 许可证类型 | 版本     |\n| ------------------------------------------------------ | ------- | ----- | ------ |\n| [tabler-icons](https://github.com/tabler/tabler-icons) | 验证码界面图标 | MIT   | 3.30.0 |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaobby%2Fsaobbycaptcha-v3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaobby%2Fsaobbycaptcha-v3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaobby%2Fsaobbycaptcha-v3/lists"}