Swift中OAuth2令牌清理异常:注销后无法正常重新登录
Swift OAuth2注销与钥匙串问题解决方案
一、解决注销后自动登录的异常
- 彻底清除OAuth2实例状态:
调用authorizer.oauth2.forgetTokens()后,手动清空实例内的令牌字段,避免内存中残留状态触发自动登录:authorizer.oauth2.forgetTokens() authorizer.oauth2.accessToken = nil authorizer.oauth2.refreshToken = nil - 销毁并重建OAuth2实例:
注销完成后直接销毁原OAuth2CodeGrant实例,重新登录时创建全新实例,不要复用旧实例:// 注销时销毁实例 authorizer.oauth2 = nil // 重新登录初始化新实例 let oauthSettings = OAuth2CodeGrantSettings( clientId: "你的ClientID", clientSecret: "你的ClientSecret", authorizeUrl: "授权地址", tokenUrl: "令牌地址", redirectUri: "回调地址" ) authorizer.oauth2 = OAuth2CodeGrant(settings: oauthSettings)
二、解决钥匙串删除失败(Error Code -25300)
错误码-25300代表errSecItemNotFound,要么条目不存在,要么权限不匹配,按以下步骤处理:
- 核对钥匙串访问组:
如果App开启了钥匙串共享,确保OAuth2实例的keychainAccessGroup和项目配置的访问组完全一致,否则无法正确操作钥匙串条目。 - 手动调用钥匙串API删除:
框架自带的删除方法失效时,直接用系统钥匙串API指定条件删除:// 删除令牌相关钥匙串条目 func deleteTokenFromKeychain(oauth2: OAuth2CodeGrant) { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: oauth2.keychainAccountForTokens, kSecAttrService as String: oauth2.keychainServiceName, kSecAttrAccessGroup as String: oauth2.keychainAccessGroup ?? "" ] SecItemDelete(query as CFDictionary) } // 删除客户端凭证相关钥匙串条目 func deleteClientCredentialsFromKeychain(oauth2: OAuth2CodeGrant) { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: oauth2.keychainAccountForClientCredentials, kSecAttrService as String: oauth2.keychainServiceName, kSecAttrAccessGroup as String: oauth2.keychainAccessGroup ?? "" ] SecItemDelete(query as CFDictionary) } - 确保在主线程执行钥匙串操作:
部分钥匙串API对线程环境有要求,尽量把删除/查询逻辑放在主线程调用。
三、访问keychainAccountForClientCredentials和keychainAccountForTokens的存储数据
可以直接通过这两个属性拿到对应的钥匙串账户名,再用系统API查询存储的原始数据:
// 查询令牌数据 func getStoredTokenData(oauth2: OAuth2CodeGrant) -> Data? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: oauth2.keychainAccountForTokens, kSecAttrService as String: oauth2.keychainServiceName, kSecReturnData as String: kCFBooleanTrue!, kSecMatchLimit as String: kSecMatchLimitOne ] var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) return status == errSecSuccess ? (result as? Data) : nil } // 查询客户端凭证数据 func getStoredClientCredentialsData(oauth2: OAuth2CodeGrant) -> Data? { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: oauth2.keychainAccountForClientCredentials, kSecAttrService as String: oauth2.keychainServiceName, kSecReturnData as String: kCFBooleanTrue!, kSecMatchLimit as String: kSecMatchLimitOne ] var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) return status == errSecSuccess ? (result as? Data) : nil }
拿到的Data是框架序列化后的内容,一般是JSON格式,可尝试转成JSON对象解析具体内容。
内容的提问来源于stack exchange,提问作者Camille Gallet
相关产品推荐
相关产品推荐

