iOS基于Auth2实现多应用SSO的问题求助(使用AuthSwift)
我帮你梳理下怎么解决这个跨App单点登录的问题,结合你用的AuthSwift和遇到的几个痛点,咱们一步步来搞定:
核心问题分析
你遇到的本质问题是:iOS 11+之后,SFSafariViewController的会话Cookie和Safari主App、其他App的SFSVC实例是隔离的;而直接调用Safari虽然能共享Cookie,但跳转体验差。所以咱们得换个思路——不依赖浏览器Cookie,而是在App间直接共享OAuth2的令牌信息,或者用官方支持的凭据共享机制。
方案一:钥匙串共享组(最推荐,体验最优)
这个方案是把OAuth2的access token、refresh token存在共享钥匙串组里,让同开发者的A、B App都能访问这些令牌,完全绕开浏览器的Cookie限制,体验和Facebook/Messenger一致。
步骤1:配置钥匙串共享权限
- 打开Xcode,分别给A、B App添加
Keychain SharingEntitlement(在Signing & Capabilities里添加)。 - 给两个App设置同一个Group ID,比如
com.yourcompany.sso-shared-group(格式一般是你的TeamID.自定义组名)。
步骤2:自定义AuthSwift的存储层
AuthSwift默认的存储是存在App沙盒里的UserDefaults,不能跨App访问,所以咱们要改成存储到共享钥匙串里。可以用KeychainAccess库(直接pod引入)来简化操作,示例代码:
import AuthSwift import KeychainAccess // 初始化共享钥匙串实例,用咱们刚才设置的Group ID let sharedKeychain = Keychain(service: "com.yourcompany.sso-shared-group") // 创建AuthSwift实例时,指定自定义存储 let authSwift = AuthSwift( consumerKey: "你的ConsumerKey", consumerSecret: "你的ConsumerSecret", requestTokenUrl: "https://你的认证服务器/oauth/request_token", authorizeUrl: "https://你的认证服务器/oauth/authorize", accessTokenUrl: "https://你的认证服务器/oauth/access_token", callbackUrl: "你的AppScheme://callback" ) // 替换默认存储为共享钥匙串存储 authSwift.storage = SharedKeychainStorage(keychain: sharedKeychain) // 自定义存储类,实现AuthSwift的存储协议 class SharedKeychainStorage: AuthSwiftStorageProtocol { private let keychain: Keychain init(keychain: Keychain) { self.keychain = keychain } func set(_ value: String, forKey key: String) { do { try keychain.set(value, key: key) } catch { print("钥匙串存储失败:\(error)") } } func string(forKey key: String) -> String? { return try? keychain.getString(key) } func remove(forKey key: String) { do { try keychain.remove(key) } catch { print("钥匙串删除失败:\(error)") } } func allKeys() -> [String] { return (try? keychain.allKeys()) ?? [] } }
步骤3:启动时自动登录
在A、B App的启动逻辑里,先检查共享钥匙串里有没有有效的令牌:
- 如果有,直接用令牌调用接口获取用户信息,跳转到首页;
- 如果没有,再触发OAuth2认证流程(推荐用下面说的
ASWebAuthenticationSession)。
方案二:Shared Web Credentials(官方网页凭据共享)
如果你的认证服务器支持,也可以用iOS官方的Shared Web Credentials机制,让App和网页共享登录凭据,同时跨App共享。
步骤1:配置Associated Domains
给A、B App添加com.apple.developer.associated-domains Entitlement,添加webcredentials:你的认证域名.com(比如webcredentials:auth.yourcompany.com)。
步骤2:配置服务器端文件
在你的认证服务器的.well-known目录下,创建apple-app-site-association文件,内容如下(替换成你的TeamID和两个App的Bundle ID):
{ "webcredentials": { "apps": ["ABCDE12345.com.yourcompany.AppA", "ABCDE12345.com.yourcompany.AppB"] } }
步骤3:在AuthSwift中集成
登录成功后,调用SecAddSharedWebCredential把用户名和令牌存储为共享凭据;在另一个App启动时,调用SecRequestSharedWebCredential获取凭据,自动完成登录。
针对你之前尝试的方案的优化
- WebView方案:放弃吧,WebView的Cookie是App沙盒私有,根本没法跨App共享,完全不适合SSO场景。
- SFSafariViewController方案:iOS 11+之后确实隔离了Cookie,所以不用纠结这个了,换成上面的钥匙串方案更靠谱。
- Safari跳转方案:如果一定要用浏览器会话,建议换成
ASWebAuthenticationSession(iOS 12+),它是苹果专门为OAuth2认证设计的API,体验比直接调用Safari好很多,而且可以配置prefersEphemeralWebBrowserSession来控制会话是否持久化。另外,结合钥匙串共享,只有当令牌失效时才会触发浏览器认证,平时直接用令牌登录,不会频繁跳转。
总结
优先推荐钥匙串共享组方案,实现起来简单,体验最接近你想要的Facebook/Messenger模式,完全不依赖浏览器的Cookie,跨App令牌共享直接可靠。
内容的提问来源于stack exchange,提问作者ki1234

