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

如何解决Xamarin iOS中SecKeyChain的InteractionNotAllowed问题?

嘿,我来帮你捋清楚这个问题的根源,以及对应的解决办法!你遇到的这种“找不到项却提示重复、删也删不掉”的矛盾情况,本质是钥匙串项的Accessible属性不匹配导致的,咱们一步步拆解解决:

问题根源

你之前的钥匙串项是用默认的SecAccessible.WhenUnlocked存储的,后来为了解决InteractionNotAllowed错误,直接把代码里所有查询、新增、删除操作的Accessible属性改成了SecAccessible.Always。但钥匙串的查询逻辑是严格匹配所有指定属性的——你用Always的条件去查,根本找不到原来用WhenUnlocked存的项;可旧项实际还在钥匙串里,所以新增同Service+Account的项时会提示DuplicateItem,删除时又因为条件不匹配找不到项,就陷入了死循环。

解决步骤

1. 先兼容读取旧属性的项

首先修改查询逻辑,暂时不要在查询时指定Accessible属性,这样才能匹配到所有同Service和Account的项,不管它原来的Accessible是什么:

private SecRecord ExistingRecordForKey(string accountName) {
    return new SecRecord(SecKind.GenericPassword) {
        Service = App.AppName,
        Account = accountName
        // 移除Accessible的硬编码,让查询匹配所有属性的同账号项
    };
}

2. 迁移旧项到新的Accessible属性

在读取方法里加入迁移逻辑:当读到旧属性的项时,取出值、删除旧项,再用SecAccessible.Always重新保存,这样后续就能统一用新属性操作了。同时还要修正你代码里的一个小bug——读取Generic字段时误用了ValueData:

public string GetValueFromAccountAndKey(string accountName, string key) {
    try {
        // 先查询不带Accessible属性的记录,覆盖旧项和新项
        var record = new SecRecord(SecKind.GenericPassword) {
            Service = App.AppName,
            Account = accountName
        };
        SecStatusCode resultCode;
        var match = SecKeyChain.QueryAsRecord(record, out resultCode);
        
        if (resultCode == SecStatusCode.Success) {
            string valueData = null;
            // 正确读取ValueData字段
            if (match.ValueData != null) {
                valueData = NSString.FromData(match.ValueData, NSStringEncoding.UTF8);
            } 
            // 正确读取Generic字段(之前的代码这里写错了)
            else if (match.Generic != null) {
                valueData = NSString.FromData(match.Generic, NSStringEncoding.UTF8);
            }

            if (!string.IsNullOrEmpty(valueData)) {
                // 如果项的属性不是Always,就执行迁移
                if (match.Accessible != SecAccessible.Always) {
                    // 删除旧属性的项
                    SecKeyChain.Remove(record);
                    // 用Always重新保存
                    var newRecord = CreateRecordForNewKeyValue(accountName, valueData);
                    SecKeyChain.Add(newRecord);
                }
                return valueData;
            }
        }
    } catch (Exception e) {
        // 保留你的日志逻辑
    }
    return string.Empty;
}

3. 修复新增/删除逻辑的兼容性

在SetValueForKeyAndAccount方法里,遇到DuplicateItem时,不要直接用带Always属性的记录去删除,而是用不带属性的查询条件找到对应项再删除:

// ... 原有代码 ...
if (result == SecStatusCode.DuplicateItem) {
    try {
        // 用不带Accessible的查询条件找项
        var duplicateRecord = new SecRecord(SecKind.GenericPassword) {
            Service = App.AppName,
            Account = accountName
        };
        SecKeyChain.Remove(duplicateRecord);
        // 删除后重新添加新属性的项
        SecKeyChain.Add(CreateRecordForNewKeyValue(accountName, value));
    } catch (Exception e) {
        // 保留你的日志逻辑
    }
}
// ... 原有代码 ...

4. 完成迁移后统一属性

等用户都完成一次读写操作(旧项全部迁移完成),你就可以把查询逻辑里的Accessible属性改回SecAccessible.Always,确保后续所有操作都用统一的属性。

额外提醒

苹果官方其实更推荐使用SecAccessible.AlwaysThisDeviceOnly(如果不需要跨设备同步),比Always更安全,你可以根据业务需求选择。

内容的提问来源于stack exchange,提问作者Vishnu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:28:29