Session Security

Odoo Auth Timeout 为什么不是“超时后直接踢下线”而已:会话寿命、闲置重验与多因子复核链路讲透

很多团队以为会话超时只有一种结果:重新登录。但 Odoo auth_timeout 实际区分总寿命、闲置超时、身份复核、整页跳转和弹窗复验,还支持把第一因子与第二因子拆开处理。

框架
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

Odoo 的 auth_timeout 不是“时间到了就 logout”这一种粗暴策略。

/home/ubuntu/odoo-temp/addons/auth_timeout/models/ir_http.pymodels/res_users.pycontrollers/main.py 可以看出,官方实际上搭了两层边界:

  • 一层是 会话总寿命,到了就要求重新登录
  • 一层是 闲置后的身份复核,不一定踢下线,但要重新证明“现在操作的人还是你”

更进一步,它还能把复核流程拆成:

  • 先验第一因子
  • 再要求第二因子
  • 且第二因子不能和第一因子重复

一句话概括:

Odoo 处理的不是“超时”本身,而是“什么时候该重新确认操作者身份”。


为什么要区分 lock_timeoutlock_timeout_inactivity

_must_check_identity() 会同时检查两类阈值:

1. lock_timeout

看的是 session 的 create_time

意思是:

  • 不管你中间有没有操作
  • 只要这次会话活得够久
  • 就必须重新登录

这更像“整段会话最长能活多久”。

2. lock_timeout_inactivity

看的是 identity-check-next

意思是:

  • 不是你登录多久了
  • 而是你是否已经闲置到足够久
  • 闲置后再继续操作时,要先做身份复核

这更像“离开座位一段时间后,回来别直接继续干敏感事”。

很多系统只做前者,所以会出现两个问题:

  • 用户明明一直在工作,却突然被整段踢出
  • 用户离开工位很久,只要会话还没过期,回来的人就能继续点

Odoo 用两套阈值分开处理,正是在平衡体验和安全。


为什么有时是重新登录,有时只是弹出复核框

_authenticate() 在发现需要重新确认身份时,不会一刀切。

它会根据 _must_check_identity() 返回的结果区分:

  • 如果是 logout,抛 SessionExpiredException
  • 如果是 check_identity,抛 CheckIdentityException

两者后果完全不同。

logout

这意味着原会话已经不值得继续信任了,用户要回到登录页重新建立完整会话。

check_identity

这意味着系统还认可当前会话上下文,但要求在继续前再确认一次身份。

所以它更像:

  • 会话没完全死
  • 但高信任状态暂时失效

这也是为什么 CheckIdentityException 对 HTTP 请求会被重定向到 /auth-timeout/check-identity,而 JSONRPC 场景会走弹窗式交互。

平台层面看,这不是页面差异,而是 同一安全规则在不同请求类型上的不同承接方式


为什么 WebSocket 活跃状态也会影响超时判断

_set_session_inactivity() 的注释写得很清楚:

  • 前端 JS 会把“用户是否活跃”通过 websocket 发回来
  • 关闭最后一个标签页、断网、连接关闭,也会触发状态更新

这很关键。

因为如果系统只看“最后一次 HTTP 请求时间”,会有两个偏差:

  • 用户明明还盯着页面、持续操作前端,但未必频繁打后端请求
  • 用户其实已经关了页面,却可能没有一条“我走了”的 HTTP 请求

Odoo 通过 WebSocket 把“人在不在场”这件事引入会话判断,做得比传统 session timeout 更细。

简单说:

它不只关心你多久没请求服务器,还关心你是否真的还在这台前端上活动。


为什么复核时还要区分第一因子和第二因子

_check_identity() 里有一段很值得看:

  • 先列出当前用户可用的认证方式 _get_auth_methods()
  • 如果这次复核要求多因子,先成功通过第一因子
  • 再把这次已经用过的方式记录到 identity-check-1fa
  • 第二次复核时,把这个方式从可选列表里移除

这意味着什么?

意味着 Odoo 不接受“同一个因子连用两次假装是多因子”。

例如:

  • 第一步已经用 password
  • 第二步就不能还是 password
  • 如果有 TOTP、totp_mail 或 webauthn,就必须换另一种证明方式

这是真正理解 MFA 的做法。

很多系统只是多弹一个框,但如果第二次还是同一种凭据,本质上安全增益很有限。Odoo 在这里把“因素不同”写进了流程约束里。


为什么前端也能知道闲置阈值

session_info()get_frontend_session_info() 都会把 lock_timeout_inactivity 传给前端。

这意味着前端不是在“猜会不会过期”,而是拿到了明确阈值,可以据此:

  • 触发活跃 / 闲置信号
  • 提前展示 UI 提示
  • 在需要时弹出身份确认交互

所以 auth_timeout 不是纯后端特性,而是一套前后端协同的会话治理机制。


新手最容易误解的 5 件事

1. 误以为超时只有“继续用”或“被登出”两种状态

其实还有“会话还在,但必须重新证明身份”。

2. 误以为用户活跃只看请求日志

源码明确把 websocket 存在感也算进去了。

3. 误以为多因子复核只是多输一次东西

真正关键的是第二次不能重复第一种认证方式。

4. 误以为所有请求遇到超时都该跳登录页

HTTP 页面请求和 JSONRPC 请求在承接方式上不一样。

5. 误以为闲置超时和会话总寿命是同一个参数

前者解决“你离开太久”,后者解决“这次会话活得太久”。


实战排错顺序

如果你碰到“用户没掉线但总被要求重新验证”或“明明在线却忽然弹身份确认”,建议按这个顺序查:

  1. 先看用户所在组配置的 lock_timeoutlock_timeout_inactivity
  2. 确认触发的是 logout 还是 check_identity
  3. 检查前端 websocket 心跳 / presence 是否正常上报
  4. identity-check-nextidentity-check-lastidentity-check-1fa 这些 session 键的变化
  5. 若启用 MFA,确认第二步有没有被错误地限制成与第一步同一种方式

一句话记忆

Odoo Auth Timeout 的核心不是“超时踢人”,而是把会话寿命、闲置状态和多因子复核揉成一条可分层处理的身份确认链。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。