先说结论
Odoo 的 google_recaptcha 不是“前端插一个 Google 小组件”这么简单。
从 /home/ubuntu/odoo-temp/addons/google_recaptcha/models/ir_http.py 可以看到,官方真正搭的是一个服务端收口流程:
- 先决定 public key 要不要下发给前端
- 请求到达时再从服务端验证 token
- 除了看
success,还看score - 还会检查 token 产生时的
action是否和当前业务动作一致 - 最后再把不同失败类型转换成不同报错
所以最短结论是:
在 Odoo 里,reCAPTCHA 不是一个 UI 控件,而是一条“前端采样、后端裁决”的风控链。
为什么 public key 不是无脑下发
_add_public_key_to_session_info() 会读取:
enable_recaptcharecaptcha_public_key
只有:
- 功能开着
- 而且 public key 存在
它才把 recaptcha_public_key 放进 session_info 或 frontend_session_info。
这个判断看起来很简单,但很有平台味道。
因为 Odoo 没把 reCAPTCHA 设计成“前端自己决定要不要展示”,而是把开关和 key 都留在后端配置里,由后端明确告诉前端:
- 该不该启用
- 启用时该拿哪把公钥
这能避免几种常见混乱:
- 前端页面还在尝试调用 reCAPTCHA,但后端其实已经关掉了
- 公钥没配好,页面仍然盲目初始化
- 测试环境和生产环境公钥混用
为什么真正关键的是服务端校验,而不是前端拿到 token
_verify_request_recaptcha_token(action) 才是主入口。
它会:
- 再次检查
enable_recaptcha - 从当前请求参数里取出
recaptcha_token_response - 取客户端 IP
- 调
_verify_recaptcha_token(ip, token, action)去 Google 验证 - 根据结果决定放行还是抛错
这说明一个核心原则:
- 前端拿到 token 只是“采样完成”
- 只有后端核验通过,业务请求才算可信
所以如果你把 reCAPTCHA 只理解成“前端上有个 token 字段”,那就低估它了。Odoo 真正在意的是:
业务动作最终要由服务端基于外部验证结果来裁决。
为什么 Odoo 不只看 success,还要看 score
_verify_recaptcha_token() 调的是 v3 验证接口,返回的不只是成功 / 失败,还有分数。
Odoo 会把结果与 recaptcha_min_score 比较:
- 分数高于阈值,判为
is_human - 分数低于阈值,判为
is_bot
这非常重要,因为 v3 的核心思想本来就不是“勾选我不是机器人”,而是“给这次行为打一分”。
所以运营上最常见的误区是:
- 把 reCAPTCHA 当成绝对真 / 假判断
但 Odoo 的实现已经明确告诉你:
- 它是风险评分
- 你的系统要决定阈值
- 阈值太高会误伤真人
- 阈值太低会放进噪音请求
换句话说,这里真正的产品决策不是“是否启用 reCAPTCHA”,而是“你愿意接受多高的风险”。
为什么还要校验 action
源码在 res_success and action and result['action'] 这段逻辑里,还会检查 token 的 action 是否等于当前验证动作。
这一步很多人会忽略,但它非常关键。
因为如果不核对 action,就可能出现一种跨场景复用:
- 用户在一个低风险页面拿到 token
- 攻击者把这个 token 拿去提交另一个高风险动作
一旦 action 不绑定具体业务操作,token 的上下文就会变模糊。
Odoo 在这里做的是:
- token 不只要是真的
- 还要证明它是“为这次动作拿到的”
这叫上下文一致性,不只是验证码有效性。
为什么不同错误要转成不同用户反馈
Odoo 没把所有失败都粗暴显示成“验证码失败”。
它会区分:
wrong_secretwrong_tokentimeoutbad_requestis_bot
对应抛出 ValidationError 或 UserError,提示也不同。
这带来两个好处:
对用户
用户能知道是:
- 请求超时了
- 参数错了
- 还是系统怀疑行为异常
对运维
你也更容易判断:
- 是配置错了私钥
- 还是前端没把 token 带上
- 还是 Google 接口波动
- 还是确实有风险流量进来
平台机制里,错误分型本身就是可运维性的一部分。
为什么“没配私钥”会被视为 no_secret
_verify_recaptcha_token() 发现 recaptcha_private_key 为空时,直接返回 no_secret。
这意味着在 Odoo 的模型里,reCAPTCHA 的真正生效条件不是“前端看到组件”,而是:
- 公钥能下发
- 私钥能校验
- 功能开关没关闭
缺任何一块,都不算完整启用。
这也是排错时很容易忽略的一点:
- 页面上看到了前端行为
- 并不代表后端风控已经真正工作
新手最容易误解的 5 件事
1. 误以为 reCAPTCHA 是前端能力
真正做放行 / 拒绝决定的是后端。
2. 误以为 success=true 就足够
Odoo 还看 score,低分仍然会拒绝。
3. 误以为 token 只要有效就能到处复用
源码明确还要检查 action 是否匹配。
4. 误以为所有失败提示都该一样
不同错误分型对用户体验和运维排查都很重要。
5. 误以为 public key 配好了就算启用完成
没有 private key,服务端根本无法形成闭环验证。
实战排错顺序
如果你碰到“页面明明带了 token,却总被拦截”这类问题,建议按这个顺序查:
- 先看
enable_recaptcha是否真的开启 - 确认 public key 和 private key 是否成对配置
- 检查请求里有没有
recaptcha_token_response - 核对当前 action 名与前端申请 token 时是否一致
- 看
recaptcha_min_score是否设得过高 - 再区分是 timeout、bad_request 还是 is_bot
一句话记忆
Odoo Google reCAPTCHA 的重点不是“页面上多一个验证码”,而是用 score、action 和服务端判定,把公开表单流量收进一条真正可调参的风控链。
DISCUSSION
评论区