Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

登录鉴权与账号体系

“账号是所有业务的基石,一次优秀的登录系统设计,需要兼顾安全防护、无缝体验以及应对多设备、状态同步的复杂性。”

面试策略: 把这道题当作架构设计题来答。不要只讲发送密码存一下 token,要从 双 Token 体系、状态机控制、安全存储 三个维度,展现对登录流程严密性的思考。

一、Token 与鉴权基础 (OAuth2/JWT)

移动端通常不再使用传统的 Session-Cookie 模型(有 CSRF 风险和跨端限制),主流方案是基于 Token 的鉴权机制。

  • JWT (JSON Web Token): 自包含的令牌,服务端签发后无需查询数据库即可校验(只要验证签名)。
    • 结构: Header(算法)+ Payload(用户 ID/过期时间)+ Signature(防篡改签名)。
    • 弱点: 一旦签发,在过期前服务端难以主动使其失效(除非引入黑名单,但这会失去 JWT 无状态的优势)。
  • OAuth2 核心流程: 用于第三方授权登录(微信/Google 登录),获取 Access Token 来访问资源。

二、双 Token 体系与无感刷新

为解决 JWT 无法撤销与长期有效带来的安全风险,业界标准是采用 双 Token (Access Token + Refresh Token) 机制:

  1. Access Token (AT): 生命周期极短(如 2 小时),每次网络请求放在 Header 中 (Authorization: Bearer <token>)。即使泄漏,风险时间也短。
  2. Refresh Token (RT): 生命周期较长(如 30 天),仅用于获取新的 AT。绝不能在普通业务接口中传输

无感刷新流程设计 (并发拦截控制): 当业务请求收到 401 Unauthorized(AT 过期)时:

  • 网络层拦截器捕获到 401。
  • 挂起当前及后续其他需要鉴权的请求。
  • 发起使用 RT 换取新 AT 的请求。
  • 刷新成功后,保存新 token,并自动重试刚才挂起的业务请求。
  • 如果 RT 也过期(返回特定错误),则清空本地登录状态,跳转到登录页。

三、登录状态机管理

客户端的登录状态往往很乱(如闪屏页、其他请求触发掉线),必须引入状态机或单向数据流进行集中管理。

未登录 (LoggedOut) 
  ↓ (输入账号密码/授权)
登录中 (LoggingIn) → 失败回 [未登录]
  ↓ (获取 Token 成功)
已登录 (LoggedIn) 
  ↓ (401触发刷新)
刷新中 (Refreshing) → 成功回 [已登录],失败去 [未登录]

使用单一可信源(如全局的 StateFlow/LiveData)来分发当前状态,UI 根据这个状态决定是展示个人中心还是弹出登录框。避免到处散落 if (isLogin())

四、本地安全存储与风险对抗

Token 是用户的钥匙,保存在本地必须做安全防护:

存储方案风险级别防御手段 / 面试加分项
明文 SharedPreferences / SQLite极高,Root 后一览无余坚决禁止
自定义加密存储中等,密钥可能被逆向提取密钥需经过混淆、分段或者配合动态下发
Android Keystore (EncryptedSharedPreferences)低,基于硬件 TEE 的密钥管理推荐方案。即使拿到磁盘文件也无法解密,密钥不出安全区

设备绑定与风控检查: 登录不仅仅是密码匹配。服务端通常还会结合你收集的设备指纹进行判定:异地登录、新设备登录、同一设备高频切换账号等,需要触发短信验证码、滑块验证等二次认证机制。

五、多设备登录与单点登录 (SSO)

  • 单点登录 (SSO): 企业内部多 App 互通登录状态。通常由一个主应用/网页提供统一鉴权,返回临时 Ticket,其他端用 Ticket 去认证中心换自己的 Token。
  • 多设备互踢:
    • 服务端维护一张 [用户ID - 设备ID - Token] 映射表。
    • 用户在 B 设备登录,服务端使得 A 设备的 Token 失效,或者通过 WebSocket/Push 主动推一条“踢出下线“指令给 A 设备。
    • A 设备收到通知,清空本地状态,弹窗提示“您的账号在其他设备登录“。

高频面试题

Q1: Access Token 和 Refresh Token 的机制是什么?为什么要用两个 Token? 为了平衡安全用户体验。单一长效 Token 泄漏风险极大且难以撤销;短效 Token 频繁过期会让用户反复登录体验极差。双 Token 用短效 Access Token 降低泄漏后的损失窗口,用长效 Refresh Token 实现静默续期,且 Refresh Token 只发往专门的刷新接口,截获概率低。

Q2: 在协程/RxJava 拦截器中,怎么处理多并发请求导致的多次 Token 刷新问题? 利用并发锁或者协程 Mutex。当第一个 401 触发刷新时,上锁,后续的 401 请求判断正在刷新中,则挂起等待。刷新成功后,通知所有等待的请求用新 Token 重试;如果刷新失败,则全部抛出未登录异常跳转登录页。

Q3: App 卸载重装后,如何保持依然处于登录状态(免密登录)? 通常通过将加密 Token 或者账号绑定凭证备份到系统级的机制中,如 Android 的 KeyChain、AccountManager,或者依靠读取稳定的硬件设备指纹作为辅助免密凭证。但出于安全合规考虑,现在的 App 大多重装后仍要求重新登录或进行验证码二次确认。

易错点 / 追问

  • Token 存在内存中没有持久化: 导致 App 被系统杀进程重启后状态丢失。
  • 未处理多进程的 Token 同步: 很多 App 有独立推送或后台进程,单进程刷新了 Token 没通知其他进程,导致 401 死循环。
  • 退出登录未通知服务端: 本地清空了 Token,但 JWT 仍然没有过期,截获这段 JWT 的黑客仍能继续请求(应当让服务端将该 Token 暂时加入黑名单)。