You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 21:43:12