4. 登录流程核心实现
登录是账号体系的入口,也是客户端与服务端交互最密集、最容易出稳定性问题的环节。本节围绕三种主流登录方式——账号密码登录、验证码登录、一键登录——拆解它们的交互链路、登录态生成方式、Token 下发结构,以及客户端本地安全存储的落地做法。三种方式共享同一套“凭证校验 → 登录态生成 → Token 下发 → 本地存储 → 后续请求携带”的主干,差异集中在凭证类型与校验路径上。
一、三种登录方式的交互链路差异
账号密码登录的凭证是“账号 + 密码”。客户端不应直接明文传输密码,通常做法是:客户端先请求服务端下发一次性随机数(nonce)或盐值,再用 SHA-256(password + nonce) 生成摘要上传。服务端用同样算法比对存储的密码哈希(如 bcrypt 结果),校验通过后进入登录态生成阶段。这条链路的关键点是防重放:nonce 必须一次性有效,过期时间建议 60 秒。
验证码登录的凭证是“手机号 + 短信验证码”。链路分两步:先调用发送接口,服务端生成 6 位数字验证码,写入 Redis 并设置 5 分钟 TTL,同时按手机号维度限流(如 60 秒内只能发一次、单日上限 10 次);用户提交后,服务端比对 Redis 中的值,一致则删除该 key 并进入登录态生成。验证码登录天然免密,但必须防止“验证码爆破”,因此校验失败次数超过 5 次应直接锁定该手机号 15 分钟。
一键登录依赖运营商网关能力。客户端集成运营商 SDK 后,在 4G/5G 蜂窝网络下可获取本机号码掩码(如 138****8888)和一个临时 token。客户端把该 token 传给业务服务端,业务服务端再向运营商服务端换取真实手机号,校验通过后直接生成登录态。一键登录的体验最好,但受网络环境限制(WiFi 下通常不可用),因此工程上一般把它作为首选,失败时降级到验证码登录。
二、登录态生成与 Token 下发结构
三种方式校验通过后,服务端统一进入登录态生成。推荐采用双 Token 结构:Access Token 有效期短(示例取 2 小时),用于业务接口鉴权;Refresh Token 有效期长(示例取 30 天),仅用于换取新的 Access Token。Access Token 用 JWT 承载,payload 中放入 uid、deviceId、iat、exp,服务端用 RS256 私钥签名,各业务服务用公钥验签,避免每次请求都查库。
Refresh Token 不放进 JWT,而是生成一个 32 字节随机串,服务端以 refresh:{uid}:{deviceId} 为 key 存入 Redis,value 记录签发时间与设备信息,TTL 设为 30 天。这样做的目的是支持“单设备登出”和“全端下线”:删除对应 key 即可让 Refresh Token 失效。Access Token 过期后,客户端用 Refresh Token 调用 /auth/refresh,服务端校验 Redis 中是否存在该 key,存在则签发新 Access Token,并可选择同时轮换 Refresh Token(旧 key 删除、新 key 写入),降低泄露风险。
Token 下发的响应体建议统一结构,便于客户端解析:
{
"code": 0,
"data": {
"accessToken": "eyJhbGciOiJSUzI1NiIs...",
"refreshToken": "a3f8c2e1b9d74056...",
"expiresIn": 7200,
"tokenType": "Bearer",
"uid": "100238",
"isNewUser": false
}
}
其中 expiresIn 单位为秒,客户端据此计算本地过期时间,提前 5 分钟触发静默刷新,避免请求因 Token 过期而失败。
三、本地安全存储方案
Token 落到客户端后,存储位置直接决定安全性。Android 上常见方案有三档:
- SharedPreferences 明文:最不安全,root 设备可直接读取,仅适合临时调试。
- EncryptedSharedPreferences:基于 Android Keystore 生成 AES 密钥,对 key/value 自动加解密,API 23+ 可用,是当前推荐的基础方案。
- Keystore + 自管理加密:用 Keystore 生成不可导出的 RSA/AES 密钥,自行加密后写入文件或 SP,灵活度最高,适合对安全要求更高的场景。
工程上建议:Access Token 放内存 + EncryptedSharedPreferences 双写,内存优先,减少磁盘读取;Refresh Token 只放 EncryptedSharedPreferences,且不随日志输出。同时注意:不要把 Token 写进 BuildConfig、strings.xml 或日志;不要在 WebView 的 localStorage 中存 Token,避免 XSS 窃取。
四、稳定性与异常处理要点
登录链路的稳定性问题大多集中在“并发刷新”和“Token 失效”两类。并发刷新指多个请求同时发现 Access Token 过期,同时发起刷新,导致 Refresh Token 被多次轮换而互相失效。解决办法是在客户端加一把单飞锁(如 Mutex 或 synchronized),只允许一个刷新请求在途,其余请求挂起等待刷新结果。
Token 失效则要区分场景:Access Token 过期走静默刷新;Refresh Token 也失效(如被服务端主动删除)时,必须清空本地凭证并跳转登录页,同时避免在登录页重复弹出提示。服务端返回 401 时,客户端应统一拦截,先尝试刷新一次,刷新失败再登出,而不是每个接口各自处理。
最后,登录接口本身要有防刷与可观测性:按 IP + 账号维度限流,记录登录成功/失败日志(含设备指纹、时间、结果码),对连续失败账号触发风控。这样三种登录方式才能在共享主干的同时,各自守住自己的风险点。