会话与登录登出:两套凭证、冷启动恢复,以及为什么过期就登出
项目 social-app
1. 背景与问题
这是一款面向日本市场的语音社交 App。用户眼中只有「登录」和「退出」。客户端里其实有三套互不替代的凭证:
- 业务会话:登录接口随用户资料一起返回,之后每次业务请求放在 Cookie 里。
- 即时通讯票:再向业务服务要一张,交给第三方 IM SDK。登录回包里也会带一份,但自动登录读的是另一处本地缓存。
- 房间音视频票:进房时下发,只服务这一次进房。到期回调只记日志,不续票。
这篇讲这三套凭证怎么来、冷启动怎么决定进首页还是登录页,以及过期之后为什么选择登出而不是刷新。游客登录、卸装后恢复同一游客号、游客转正、注销账号、资料和选圈子,是在这套底座上补上的;会话归档、Cookie、登出去重和即时通讯自动登录本身是团队方案。
2. 三套凭证
| 种类 | 从哪来 | 放在哪 | 失效时怎么处理 |
|---|---|---|---|
| 业务会话 | 登录回包 | 用户归档和内存 | 业务码触发登出,不换新凭证 |
| 即时通讯票 | 再向业务服务要;回包里也有一份 | 偏好设置。归档里那份不用于自动登录 | 需要换票的错误会清掉本地票再要;尝试登录失败最多再要 5 次 |
| 房间票 | 进房接口 | 房间模型 | 到期只记一条错误日志并上报,不续 |
回包里没有刷新用的凭证,也没有业务会话的过期时间。客户端因此无法在本地判断「这张票还剩多久」。
游客凭证是例外:它进系统钥匙串,另外还有一份游客归档,用来认出「现在是游客」以及卸装重装后找回同一个号。普通登出故意留着这两份,否则重装就变成新游客。
3. 登录入口和成功之后
登录页上的通道是 Apple、Google、LINE、Twitter 和游客。没有手机号、短信或邮箱登录。资料里的手机和邮箱来自资料接口,不是登录通道。
三方登录先拿授权,再交给对应的业务登录接口,成功返回完整用户。Apple 的全名只在第一次授权时给出,所以会缓存在钥匙串里。游客登录如果钥匙串里已经有凭证就带上,否则空着去申领。
资料未完成的新用户会先去资料页(昵称、性别、出生日期、头像),再到选圈子。选圈子提交时如果还没有账号,会在这里补一次游客登录。游客登录失败时展示服务端错误,而不是一直转圈。已经有完整资料的用户则直接发登录通知。
根页收到通知之后才切首页、绑定推送,并尝试登录即时通讯。业务登录接口返回时,即时通讯还没登完。
4. 冷启动:只看本地,不问服务器
启动时会准备日志、推送、三方平台和即时通讯 SDK,但这里不登录。账号容器第一次被用到时,把归档里的用户解出来。根页再做决定:
没有一步用会话标识去问服务器「这张票还有效吗」。资料是否完成:审核包看新手标记,否则看性别是否仍未知。因此「归档还在,但资料没填完」会回到登录页,而不是直接打开资料页。游客可以凭钥匙串再登,再走进资料流。
5. 过期就登出,不刷新
发请求之前不检查会话是否过期。这次请求的业务码说无效、过期、非法、被其他端踢下线,或者账号封停,就立刻发登出通知。注释写的是「过期了」,实际把非法登录、被踢和「请求过期」都算进去。
多个请求一起失败时,靠一个一次性标志:已经发过通知就忽略,下次登录成功再复位。即时通讯的被踢弹窗还在时,先不发通知,等用户点确定。登出之后不会自动重放刚才那次请求,因为没有「先刷新再重放」这条路。
这是当时选定的设计:客户端保持简单,有效性完全交给服务端。代价写在第 8 节,那些改进都还没有做。
6. 即时通讯票
冷启动或根页出现后:如果偏好里的通讯账号和当前用户一致,就用已有的票自动登录;否则向业务服务要一张新的再登。所以登录回包里即使已经带着通讯票,客户端仍常常会再要一次。
被踢的处理分三种:封禁先提示「账号已被封禁」再登出;其他由服务端发起的踢,直接登出,没有额外文案;不是服务端踢的(其他设备登录)先提示再登出。自动登录若遇到需要换票的错误,清掉本地票再走一遍;账号封禁则登出;操作过于频繁则尝试回传即时通讯 SDK 的日志。
业务请求上的「被其他端踢下线」不会弹出「已在其他设备登录」,只发登出通知,用户直接看到登录页。和即时通讯被踢的提示不一致。
票或账号为空时,尝试登录只会记一条日志,但「正在登录」可能已经置上。超过 5 次仍失败时,这个标志也可能没有复位。两种情况都要走到登出或杀掉进程才能再登。
7. 主动退出和强制登出
用户在设置里确认退出:如果还在语音房,先退房;然后先登出即时通讯。即时通讯失败就提示「退出登录失败」,本地会话保持不动。成功之后才解绑推送,并调用业务登出。业务登出是发出去就继续,不等它成功;通知和清本地多半已经发生。接口失败会提示,纯网络失败不再额外上报。
强制登出来自业务码,或即时通讯被踢,或自动登录发现账号封禁。这条路不调用业务登出,不解绑推送,也不清理三方 SDK 的登录态。
| 清理项 | 用户主动退出 | 强制登出 |
|---|---|---|
| 先退出语音房 | 会 | 通知之后若仍在房则退 |
| 即时通讯登出 | 必须先成功 | 清本地时再做一次 |
| 解绑推送 | 即时通讯成功后做 | 不做 |
| 调用业务登出 | 会,但不等成功 | 不做 |
| 清理三方 SDK 登录态 | 不做 | 不做 |
| 删用户归档 | 即时通讯成功后做 | 会 |
| 保留游客钥匙串 | 会,为了重装恢复 | 会 |
根页收到通知后的共同清理是:拆掉首页、复位「是否进过首页」、切到登录页、把好友列表的刷新时间打回起点、删掉用户归档、清掉内存里的配置(下次请求会重新过配置门)、取消正在进行的动态上传并丢掉内存草稿、角标归零。圈子的归档文件这次清理未必会删,换账号后有短暂串数据的风险。
8. 取舍与可改进之处
下面每一条都是方向,不是已经做完的改动。
| 现在的做法 | 问题 | 改进方向 |
|---|---|---|
| 过期、非法、被踢、封停、请求过期都登出 | 「请求过期」更像时钟或时间戳问题,一并登出可能过激 | 把「请求过期」从登出集合里拆出去,先确认是不是有意 |
| 没有刷新,失败的请求也不重放 | 一次过期就要整段重新登录 | 单次刷新:进行中的请求先排队,换到新会话后再重放 |
| 强制登出不解绑推送、不调业务登出、不清三方 SDK | 旧设备可能继续收到推送,服务端会话也可能还在 | 主动退出和强制登出走同一条清理 |
| 业务码被踢不提示,即时通讯被踢会提示 | 同一种「被顶下来」体验不一致 | 两种踢都先说明原因再进入登录页 |
| 业务会话放在文档目录的归档里 | 和钥匙串里的游客凭证、设备标识不是同一安全级别 | 会话标识改放钥匙串 |
| 即时通讯重试超过 5 次,进行中的标志可能还在 | 之后每次准备登录都以为正在登录 | 放弃时一定复位;票为空时不要先把标志置上 |
| 房间票到期只记日志 | 长房间里可能无声,而且日志里不该出现票本身 | 到期前向业务再要一张并更新进房;日志只记「到期」,不记票 |
另外两件已经能看出来、同样还没改:归档里的通讯票和偏好里实际用来登录的那份可能不一致;强制登出时房间页会不会被置空,取决于通知和清用户谁先谁后。
9. 游客、转正、注销和新手
游客登录:钥匙串里已有凭证就带上,否则首次申领。成功后凭证写入钥匙串,并另存一份游客归档,卸装重装后仍是同一个游客号。
游客转正用 Apple、Google、LINE、Twitter 之一绑定。成功就删掉游客归档和钥匙串里的游客凭证,之后用该三方账号登录。绑定失败只提示,游客态保留。
注销账号先查登录类型,再按原来的渠道重新授权,然后删号。确认页也会发同一套登出通知。
新手后半段是资料页再选圈子。资料包括昵称、性别、出生日期和头像。选圈子提交时如果还没有账号,就在这一步补游客登录。这一步失败要展示服务端返回的错误。选圈子是现在的主路径;更早还有选游戏,后来被选圈子取代。
普通登出不清游客钥匙串。这是为了重装后还能回到同一个游客号,不是漏删。