同一个 token 能不能在下一条 QA 用例里接着用?取决于是否已绑定具体请求、是否超过官方有效期 —— 判断清楚就能省掉一批不必要的重复求解,仅限自有内部 endpoint、有效期内使用。
安全范围: 本指南仅适用于你自有或经授权的 QA、staging 与预发布环境。内容覆盖针对你自己 CAPTCHA 集成的诊断、测试与可观测性模式 — 不涉及第三方站点或未授权流程。
三种 token 的有效期与能不能缓存
三种类型的 token 有效期都很短,规则也各不相同,直接决定缓存策略怎么定:
| CAPTCHA 类型 | Token 有效期 | 能否缓存 | 说明 |
|---|---|---|---|
| reCAPTCHA v2 | 约 120 秒 | 有限 | 多数站点只接受同一 token 提交一次 |
| reCAPTCHA v3 | 约 120 秒 | 有限 | 同一站点不同请求返回的分数可能不同 |
| Cloudflare Turnstile | 约 300 秒 | 有效期内可以 | 只要没过期,同一 token 可反复复用 |
Turnstile 窗口最宽松,最适合优先缓存;reCAPTCHA v2/v3 能否复用取决于站点校验逻辑,先测试再决定。
QA 缓存实现:按 TTL 管理内存字典
QaTokenCache 只做两件事:按 key 存 token,读取时检查是否过了自定义 TTL。TTL 要比官方有效期短一截 —— Turnstile 官方给 300 秒,内部设 90 秒更稳妥,避免用到临界失效的 token。
import time
class QaTokenCache:
def __init__(self, default_ttl=90):
self.store = {}
self.default_ttl = default_ttl
def get(self, key):
item = self.store.get(key)
if not item:
return None
token, exp = item
if time.time() >= exp:
self.store.pop(key, None)
return None
return token
def put(self, key, token, ttl=None):
self.store[key] = (token, time.time() + (ttl or self.default_ttl))
在内部 QA endpoint 校验缓存的 token
拿到 token 后(新求解或缓存读出),提交到 QA 后端 endpoint 校验 secret、sitekey 是否匹配,并记录命中率 —— 骤降通常是 TTL 设短了或参数未归一化。
观测指标:从命中率到全链路追踪
给每个 qa_case_id 记录 token 来源(缓存/新求解)、求解耗时、后端状态码;规模变大后加一层结构化日志和 correlation id(如 OpenTelemetry),按 id 重放场景可省一半排查时间。
常见故障排查
| 问题 | 处理方式 |
|---|---|
| token 在 QA 环境中过期 | 只在有效期内复用,不在生产环境复用 |
| backend 拒绝 token | 比对 sitekey、action、secret 是否一致 |
| 内部缓存命中率偏低 | 检查 TTL 是否短于实际有效期,或参数未归一化 |
| 多 worker 结果不一致 | 换 Redis 等跨进程存储,统一 TTL 规则 |
上线前检查清单
- 测试范围限定在已获授权的资源。
- key 存放在 CI secret 仓库或 vault,不进源代码。
- 每次运行记录耗时与状态码。
- 瞬时错误配置幂等重试与上限。
- 测试能在 CI 中反复重现。
Python 示例:一次最小可运行的 QA 调用
在自己的 staging 环境里,用 CaptchaAI 跑通一次 CAPTCHA widget 测试的最小闭环:提交任务拿 taskId,再轮询拿最终 token。
import os
import requests
API_KEY = os.environ['CAPTCHAAI_KEY']
QA_PAGE_URL = os.environ['QA_PAGE_URL'] # 例如 https://staging.example.com/qa-login
QA_SITE_KEY = os.environ['QA_SITE_KEY']
def submit_qa_recaptcha() -> str:
payload = {
'clientKey': API_KEY,
'task': {
'type': 'NoCaptchaTaskProxyless',
'websiteURL': QA_PAGE_URL,
'websiteKey': QA_SITE_KEY,
},
}
response = requests.post(
'https://api.captchaai.com/createTask',
json=payload,
timeout=30,
)
response.raise_for_status()
return response.json()['taskId']
def fetch_qa_result(task_id: str) -> dict:
payload = {'clientKey': API_KEY, 'taskId': task_id}
response = requests.post(
'https://api.captchaai.com/getTaskResult',
json=payload,
timeout=30,
)
response.raise_for_status()
return response.json()
常见问题
这套流程会不会碰到生产流量?
不会。所有示例都假设跑在 staging.example.com 这类已获授权的环境里,不直接对生产环境跑测试。
CaptchaAI key 能不能直接写进测试脚本?
不行。要通过 CI secret 管理器或 vault 注入;已提交进代码仓库的 key 必须立刻轮换。
reCAPTCHA v2 的 token 一定不能缓存吗?
不一定,看目标站点是否只做一次性校验。先用同一 token 重复提交测试,能通过再对该站点启用缓存。
轮询遇到瞬时错误该怎么重试?
幂等重试配合指数退避(1 秒、2 秒、4 秒)并设上限。网络错误、5xx 和 ERROR_NO_SLOT_AVAILABLE 适合重试;余额不足或 key 失效不应重试。
更多 QA 相关指南
在自己的环境里,用 CaptchaAI 验证这套缓存与复用流程。