AppAuth-iOS-ObjC:OIDAuthState归档与解归档失败求助
问题根源与解决方案
核心问题是用NSUnicodeStringEncoding将二进制归档数据转换为NSString的方式本身会破坏原始数据:归档后的NSData是二进制字节流,并非合法的Unicode文本,转字符串过程中系统会对非法Unicode字节做容错替换,导致还原时数据不一致,最终无法完成解归档。
从日志也能看出端倪:原NSData长度15860,转成NSString后长度7930(Unicode双字节特性),但转回NSData时长度变成15862,说明已有字节被篡改,自然触发不了OIDAuthState的initWithCoder方法。
推荐解决方案:直接将NSData存入Keychain(最优)
Keychain本身支持存储NSData,不需要额外转字符串,减少编码损耗,更高效安全。
存储方法
- (BOOL)saveAuthStateToKeychain:(OIDAuthState *)authState { NSError *error; NSData *authStateData = [NSKeyedArchiver archivedDataWithRootObject:authState requiringSecureCoding:NO error:&error]; if (!authStateData || error) { NSLog(@"归档失败: %@", error.localizedDescription); return NO; } // Keychain存储配置(可根据你的业务调整属性) NSDictionary *query = @{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrAccount: @"AppAuth_AuthState", (__bridge id)kSecValueData: authStateData, (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly }; // 先清理旧数据避免冲突 SecItemDelete((__bridge CFDictionaryRef)query); OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); return status == errSecSuccess; }
读取方法
- (OIDAuthState *)loadAuthStateFromKeychain { NSDictionary *query = @{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrAccount: @"AppAuth_AuthState", (__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue, (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne }; CFDataRef dataRef = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&dataRef); if (status != errSecSuccess || !dataRef) { NSLog(@"读取Keychain失败: %d", (int)status); return nil; } NSData *authStateData = (__bridge_transfer NSData *)dataRef; NSError *error; OIDAuthState *authState = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDAuthState class] fromData:authStateData error:&error]; if (error) { NSLog(@"解归档失败: %@", error.localizedDescription); } return authState; }
备选方案:用Base64转字符串(若必须转文本存储)
如果业务场景要求必须存字符串,改用Base64编码(专门用于二进制转安全文本的标准方式,不会丢失数据):
转字符串方法
- (NSString *)authStateToString:(OIDAuthState *)authState { NSError *errRet; NSData *authStateData = [NSKeyedArchiver archivedDataWithRootObject:authState requiringSecureCoding:NO error:&errRet]; if (!authStateData || errRet) { NSLog(@"归档失败: %@", errRet.localizedDescription); return nil; } return [authStateData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; }
还原方法
- (OIDAuthState *)stringToAuthState:(NSString *)authString { NSData *authStateData = [[NSData alloc] initWithBase64EncodedString:authString options:NSDataBase64DecodingIgnoreUnknownCharacters]; if (!authStateData) { NSLog(@"Base64解码失败"); return nil; } NSError *errRet; OIDAuthState *authState = [NSKeyedUnarchiver unarchivedObjectOfClass:[OIDAuthState class] fromData:authStateData error:&errRet]; if (errRet) { NSLog(@"解归档失败: %@", errRet.localizedDescription); } return authState; }
内容的提问来源于stack exchange,提问作者mjswan
相关产品推荐
相关产品推荐

