Xamarin Forms迁移MAUI后,SecureStorage.GetAsync读取旧值返回null
解决方案:Xamarin Forms迁移MAUI后SecureStorage读取旧数据问题
Android 端处理
MAUI 的 SecureStorage 在 Android 上默认使用了和 Xamarin Forms 不同的存储容器,即便 App ID 一致,也会导致无法读取旧数据。可以通过自定义 ISecureStorage 实现兼容旧存储:
创建自定义 SecureStorage 实现,直接访问 Xamarin Forms 曾使用的 KeyStore 条目:
using Android.Security.Keystore; using Java.Security; using Javax.Crypto; using Javax.Crypto.Spec; using System.Text; public class LegacySecureStorage : ISecureStorage { private const string KeyStoreName = "AndroidKeyStore"; private const string LegacyAliasPrefix = "Xamarin.Forms.SecureStorage_"; public async Task<string> GetAsync(string key) { try { KeyStore keyStore = KeyStore.GetInstance(KeyStoreName); keyStore.Load(null); string legacyAlias = LegacyAliasPrefix + key; if (!keyStore.ContainsAlias(legacyAlias)) return null; var entry = keyStore.GetEntry(legacyAlias, null) as KeyStore.SecretKeyEntry; if (entry == null) return null; SecretKey secretKey = entry.SecretKey; Cipher cipher = Cipher.GetInstance("AES/GCM/NoPadding"); cipher.Init(Cipher.DecryptMode, secretKey); var prefs = Android.App.Application.Context.GetSharedPreferences("Xamarin.Forms.SecureStorage", Android.Content.FileCreationMode.Private); string encryptedValue = prefs.GetString(key, null); if (string.IsNullOrEmpty(encryptedValue)) return null; byte[] encryptedBytes = Convert.FromBase64String(encryptedValue); byte[] decryptedBytes = cipher.DoFinal(encryptedBytes); return Encoding.UTF8.GetString(decryptedBytes); } catch (Exception) { return null; } } public Task SetAsync(string key, string value) => SecureStorage.Default.SetAsync(key, value); public Task RemoveAsync(string key) => SecureStorage.Default.RemoveAsync(key); public Task RemoveAllAsync() => SecureStorage.Default.RemoveAllAsync(); }在 MAUI 启动时注册这个自定义实现:
// 在 MauiProgram.cs 的 CreateMauiApp 方法中 builder.Services.AddSingleton<ISecureStorage, LegacySecureStorage>();
此方案既可以读取旧的Xamarin Forms存储数据,新数据也会用MAUI默认方式存储。
iOS 端处理
TestFlight 构建读取失败原因
本地部署可读取但TestFlight不行,是因为TestFlight构建启用了App Attest或数据隔离相关安全机制,导致MAUI的SecureStorage无法访问Xamarin Forms存在NSUserDefaults里的数据(Xamarin Forms的SecureStorage在iOS底层就是NSUserDefaults,仅添加了前缀)。
兼容方案
兼容旧数据读取逻辑:
读取数据时先尝试MAUI的SecureStorage.GetAsync,若返回null,再用NSUserDefaults读取旧数据,读取成功后同步到MAUI的SecureStorage中:public async Task<string> GetLegacyOrNewValue(string key) { string value = await SecureStorage.Default.GetAsync(key); if (!string.IsNullOrEmpty(value)) return value; string legacyKey = $"Xamarin.Forms.SecureStorage_{key}"; var defaults = Foundation.NSUserDefaults.StandardUserDefaults; string legacyValue = defaults.StringForKey(legacyKey); if (!string.IsNullOrEmpty(legacyValue)) { await SecureStorage.Default.SetAsync(key, legacyValue); defaults.RemoveObject(legacyKey); defaults.Synchronize(); } return legacyValue; }配置Entitlements.plist:
- 避免添加
com.apple.security.application-groups(除非业务必需),否则旧数据不在该组内会无法读取。 - 确保
keychain-access-groups包含App ID前缀,与Xamarin Forms配置一致:<key>keychain-access-groups</key> <array> <string>$(AppIdentifierPrefix)com.yourcompany.yourapp</string> </array>
- 避免添加
通用建议
- 所有读取操作先兼容旧数据,读取成功后同步到MAUI存储系统,后续统一使用MAUI的
SecureStorage方法,减少重复兼容逻辑。 - 测试时覆盖完整升级场景:从Xamarin Forms正式版本升级到MAUI的TestFlight/正式版本,验证数据读取是否正常。
内容的提问来源于stack exchange,提问作者dmc
相关产品推荐
相关产品推荐

