用户登录与账户:AccountManager、OAuth2.0、多用户切换

车载系统里的用户登录,跟手机上的完全不是一回事。你想想看,手机基本是「一个人用到底」,但车是全家共享的。爸爸开车、妈妈开车、孩子可能也要用——每个人的座椅位置、音乐偏好、导航历史都不一样。所以车载的账户体系,核心就三个事:怎么存账户信息、怎么安全登录、怎么快速切换。

我最早做车载项目时,团队直接照搬了手机端的登录方案。结果呢?用户一换人,整个系统就乱套了。后来才明白,车载场景下,多用户切换不是附加功能,而是核心体验

AccountManager:车载端的账户管家

Android 系统自带的 AccountManager,说白了就是一个集中管理账户的「保险柜」。它不存密码明文,而是存 token。我习惯用它来管理车载端的账户凭证,因为系统层已经帮我们处理了跨进程访问和加密存储。

核心要点:AccountManager 只存凭证,不处理登录逻辑。登录的 UI 和网络请求,得我们自己写。

举个例子,添加一个账户:

AccountManager am = AccountManager.get(context);
Account account = new Account("user_001", "com.car.auth");
am.addAccountExplicitly(account, null, null);

这里有个坑——addAccountExplicitly 需要 MANAGE_ACCOUNTS 权限。我在项目中遇到过,有些车机厂商把权限收得很紧,导致第三方应用无法直接添加账户。解决办法是用 AccountManager.addAccount() 配合 Intent 回调,让系统弹窗授权。

个人习惯:我会把 AccountManager 封装成一个单例,所有账户操作都走这个类。这样后续换存储方案时,只需要改这一个地方。

OAuth2.0:车载端的授权流程

OAuth2.0 在车载端最大的挑战是什么?是「无浏览器环境」。很多车机没有预装 Chrome,或者系统 WebView 版本太老。我建议直接用 Authorization Code + PKCE 流程,这样即使没有浏览器,也能安全完成授权。

为什么用 PKCE?因为车载端的 client secret 很容易被反编译拿到。PKCE 用动态生成的 code verifier 替代了 secret,安全性高了一个档次。

流程大致是这样:

  1. 车载应用生成一个 code verifier(随机字符串)
  2. 对 verifier 做 SHA256 哈希,得到 code challenge
  3. 打开系统浏览器或 WebView,跳转到授权页,带上 challenge
  4. 用户登录后,服务端返回 authorization code
  5. 车载应用用 code + verifier 去换 access token
// 生成 code verifier
String verifier = generateRandomString(64);
String challenge = Base64.encodeToString(
    sha256(verifier.getBytes()), Base64.URL_SAFE | Base64.NO_PADDING
);

// 构建授权 URL
Uri authUri = new Uri.Builder()
    .scheme("https")
    .authority("auth.car.com")
    .path("/oauth2/authorize")
    .appendQueryParameter("response_type", "code")
    .appendQueryParameter("client_id", "car_app")
    .appendQueryParameter("code_challenge", challenge)
    .appendQueryParameter("code_challenge_method", "S256")
    .appendQueryParameter("redirect_uri", "carapp://oauth2callback")
    .build();
我曾经踩过的坑:WebView 里处理 OAuth 回调时,一定要拦截 shouldOverrideUrlLoading,而不是等页面加载完。否则用户已经登录成功了,你的应用还在那傻等。

多用户切换:从「登录」到「切换」

车载的多用户切换,跟手机上的「多用户」完全是两码事。手机多用户是隔离的,每个用户有自己的应用和数据。但车载上,用户切换必须无缝——你不能让驾驶员停车等切换完成。

我设计的方案分三层:

层级 职责 实现方式
账户层 管理 token、用户信息 AccountManager + 本地加密存储
会话层 管理当前活跃用户 UserManager + 自定义 SessionManager
应用层 响应切换事件 BroadcastReceiver + LiveData

切换时,应用层需要做三件事:

  • 保存当前用户的状态(播放列表、导航路线等)
  • 清除敏感数据(比如支付信息、通话记录)
  • 加载新用户的偏好设置

嗯,这里要注意——不要每次切换都重新登录。我见过有些方案,用户一切换就弹登录页,体验极差。正确的做法是:后台维护多个用户的 token,切换时直接换 token,只有 token 过期才要求重新登录。

避坑指南:多用户切换时,MediaSession 的绑定关系很容易出问题。我曾经遇到过,切换用户后音乐还在旧用户的会话里播放。解决办法是在切换时主动释放旧用户的 MediaSession,并重新绑定新用户。

SVG 流程图:用户登录与账户体系

车载用户登录与账户体系架构 AccountManager 凭证存储与生命周期 OAuth2.0 + PKCE 安全授权与 token 管理 多用户切换 无缝切换与状态恢复 本地加密存储 Android Keystore + AES 加密 Token 刷新机制 Refresh Token + 自动重试 会话管理 SessionManager + LiveData 通知 应用层适配 MediaSession 绑定 | 用户偏好加载 | 敏感数据清除 三层架构:账户层 → 会话层 → 应用层

这张图展示了我习惯的分层架构。最上层是三个核心模块,中间是具体实现,最下面是应用层需要响应的内容。说白了,就是上层定策略,中层做实现,下层做适配

最后说一句,车载的账户体系,稳定比功能重要。我见过太多项目,为了加一个「扫码登录」的功能,把整个账户模块搞崩了。记住,用户开车时最不想看到的就是「登录失败,请重试」。

公众号:蓝海资料掘金营,微信deep3321