上一篇
安卓单点登录如何实现
- 行业动态
- 2025-04-24
- 11
集成第三方SDK或OAuth服务,获取Token存储并传递至各应用,后端验证Token实现免密
单点登录(SSO)基础概念
单点登录(Single Sign-On)指用户在一处登录后,无需重复输入凭证即可访问其他关联系统,其核心原理是通过共享认证状态或统一鉴权中心实现多应用间的无缝登录。
安卓实现SSO的常见方式
基于第三方平台(如Google、Facebook)
平台 | 核心流程 |
---|---|
集成Google Sign-In SDK 用户登录后获取 idToken 将Token传递给后端验证 后端生成全局Session并返回给客户端 | |
集成Facebook SDK 通过 LoginButton 触发登录获取 AccessToken 验证Token并建立全局会话 |
优势:快速集成,依赖成熟方案,减少维护成本。
限制:需依赖第三方服务,可能存在隐私或合规风险。
自建SSO系统
(1) 架构设计
- 认证中心:负责用户登录、Token生成与验证(如OAuth 2.0 Server)。
- 客户端(安卓):存储Token并携带Token访问其他服务。
- 资源服务器:验证Token合法性后提供数据。
(2) 关键流程
用户首次登录:
- 客户端请求认证中心(如
/oauth/token
)。 - 认证中心返回
Access Token
和Refresh Token
。 - 客户端存储Token(建议加密存储,如
SharedPreferences
+AES)。
- 客户端请求认证中心(如
跨应用共享登录状态:
- 通过广播(BroadcastReceiver)或文件共享传递Token。
- 应用A登录后,将Token写入公共文件,应用B启动时读取该文件。
Token刷新:
- 当
Access Token
过期时,使用Refresh Token
向认证中心申请新Token。 - 示例代码(OkHttp拦截器):
public class TokenInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { // 从SP读取Token String token = SPUtils.getString("access_token"); Request request = chain.request() .newBuilder() .addHeader("Authorization", "Bearer " + token) .build(); return chain.proceed(request); } }
- 当
(3) 安全注意事项
- 存储安全:使用
EncryptedSharedPreferences
或Android Keystore存储敏感数据。 - 防劫持:Token需设置短期有效期(如15分钟),并绑定设备唯一标识(如
device_id
)。 - 网络传输:所有API请求必须使用HTTPS。
技术选型对比
方案类型 | 开发成本 | 灵活性 | 安全性 | 适用场景 |
---|---|---|---|---|
第三方SSO | 低 | 低 | 中 | 快速上线,无复杂权限需求 |
自建OAuth 2.0 | 高 | 高 | 高 | 多应用协同,高安全要求 |
常见问题与解答
问题1:自建SSO系统中,如何防止Token被劫持?
解答:
- 缩短Token有效期:将
Access Token
有效期设置为5-15分钟,降低盗用风险。 - 绑定设备信息:在Token中嵌入设备唯一标识(如Android ID的哈希值)。
- HTTPS强制通信:确保所有API请求通过HTTPS传输,防止中间人攻击。
- 校验IP/UA:后端记录登录IP和User-Agent,异常请求时触发二次验证。
问题2:多个安卓应用如何共享同一个SSO会话?
解答:
- 共享存储:通过文件共享或ContentProvider在应用间传递Token。
- 示例:将Token写入
/sdcard/.sso_token
(需申请存储权限)。
- 示例:将Token写入
- 广播机制:应用A登录后发送自定义广播(如
com.example.ACTION_LOGIN_SUCCESS
),其他应用通过BroadcastReceiver
接收并读取Token。 - 账号管理器(AccountManager):使用系统级账户管理,适合同一开发者的多应用。
代码示例(自建SSO简化版)
客户端存储Token
// 使用EncryptedSharedPreferences存储Token public void saveToken(String token) { EncryptedSharedPreferences sp = EncryptedSharedPreferences.create( "sso_prefs", MasterKey.builder().setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(), context ); sp.edit().putString("access_token", token).apply(); }
后端验证Token(伪代码)
// Java Spring Boot示例 @PostMapping("/validateToken") public ResponseEntity<?> validateToken(@RequestHeader("Authorization") String token) { Claims claims = JwtUtil.parseToken(token.replace("Bearer ", "")); if (claims != null && claims.getExpiration().after(new Date())) { return ResponseEntity.ok().build(); // 验证通过 } return ResponseEntity.status(401).build(); // 验证失败 }