Flutter OpenHarmony 三方库适配实战:flutter_secure_storage 安全存储

一、项目信息

项目内容
仓库名称flutter_secure_storage
仓库地址https://atomgit.com/oh-flutter/flutter_secure_storage
上游版本flutter_secure_storage 9.2.4
OHOS 插件版本1.2.2
OHOS TAG9.2.4-ohos.1
方法通道plugins.it_nomads.com/flutter_secure_storage
事件通道plugins.it_nomads.com/flutter_secure_storage/events
构建产物entry-default-unsigned.hap

安全存储不是普通键值库。它需要同时考虑 Dart API、平台实现、密钥生命周期、加密算法和错误恢复。OHOS 适配保留上游 FlutterSecureStorage API,把 write、read、delete 等操作桥接到 ArkTS 加密首选项。

二、monorepo 结构

flutter_secure_storage/
├── flutter_secure_storage/                  # 上游主包 Dart API
├── flutter_secure_storage_ohos/             # OHOS 插件子包
│   ├── lib/
│   │   ├── flutter_secure_storage_ohos.dart
│   │   └── options/ohos_options.dart
│   └── ohos/src/main/ets/components/plugin/
│       ├── FlutterSecureStorageOhosPlugin.ets
│       ├── FlutterSecureStorage.ets
│       └── ciphers/
├── flutter_secure_storage_platform_interface/
├── flutter_secure_storage_linux/
├── flutter_secure_storage_macos/
├── flutter_secure_storage_web/
└── flutter_secure_storage_windows/

业务工程依赖 flutter_secure_storage_ohos 子包,主包负责提供统一的 FlutterSecureStorage API。

三、插件入口与通道

const METHOD_CHANNEL = 'plugins.it_nomads.com/flutter_secure_storage';
const EVENT_CHANNEL =
  'plugins.it_nomads.com/flutter_secure_storage/events';

onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.secureStorage = new FlutterSecureStorage(context, new Map());
  this.channel = new MethodChannel(
    binding.getBinaryMessenger(),
    METHOD_CHANNEL,
    StandardMethodCodec.INSTANCE,
  );
  this.channel.setMethodCallHandler(this);

  this.eventChannel = new EventChannel(
    binding.getBinaryMessenger(),
    EVENT_CHANNEL,
    StandardMethodCodec.INSTANCE,
  );
  this.eventChannel.setStreamHandler(this);
}

方法通道处理 write、read、readAll、containsKey、delete、deleteAll。写入、删除和删除全部操作完成后,插件会通过事件通道通知对应键的监听器:

private notifyListeners(key: string, value: string | null): void {
  if (key === '*') {
    this.listeners.forEach((sinks, currentKey) => {
      sinks.forEach((sink) => sink.success({
        key: currentKey,
        value: null,
      }));
    });
    return;
  }

  this.listeners.get(key)?.forEach((sink) => sink.success({
    key,
    value,
  }));
}

四、Dart API 使用

import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

Future<void> secureDemo() async {
  const storage = FlutterSecureStorage();

  await storage.write(key: 'token', value: 'mock-token');
  final token = await storage.read(key: 'token');
  final exists = await storage.containsKey(key: 'token');

  debugPrint('token=$token, exists=$exists');
}

OHOS 专属配置:

const storage = FlutterSecureStorage(
  ohOptions: OhosOptions(
    encryptedSharedPreferences: true,
    resetOnError: true,
  ),
);

OhosOptions 会随每次方法调用传递给 ArkTS。resetOnError 只解决无法恢复的解密异常,不应被当作业务数据回滚机制;写入前仍应保证值已经完成业务校验。

五、加密实现

OHOS 原生实现包含多个加密组件:

ciphers/
├── StorageCipherFactory.ets
├── StorageCipher18Implementation.ets
├── StorageCipherGCMImplementation.ets
├── RSACipher18Implementation.ets
└── RSACipherOAEPImplementation.ets

适配流程可以概括为:

  1. Dart 调用方法通道并传入 key、value 和平台 options。
  2. ArkTS 根据 options 选择加密存储策略。
  3. 使用系统加密能力保护数据后写入首选项。
  4. 读取时先判断 key 是否存在,再解密并返回。
  5. 删除或写入完成后,通过事件通道通知监听器。

这里的重点不是把 AES、RSA 类名照搬到 OHOS,而是确认密钥生成、填充方式、随机数和错误分支都符合系统 API 的实际约束。算法切换可能导致旧密文无法读取,因此升级前必须准备迁移方案。

六、权限与安全边界

当前实现使用应用自身的加密首选项,不需要额外 OHOS 权限。但安全存储仍然是本地安全边界,不等于服务端安全:

  • 不要把长期有效的支付凭证、私钥或管理员 token 直接保存在本地。
  • 账号切换时应使用不同 key 前缀或显式删除旧数据。
  • 应用卸载、恢复备份、系统迁移和算法升级都可能影响数据可用性。
  • 解密失败并开启 resetOnError 会清除无法恢复的数据,UI 层应给出可理解的提示。

七、测试与构建

cd flutter_secure_storage_ohos
flutter analyze --no-fatal-infos
flutter test
cd example
flutter build hap --debug --no-codesign

本次验证:

  • 示例工程分析没有错误,存在少量上游代码的 lint 提示。
  • 示例工程成功生成 entry-default-unsigned.hap。

安装后至少验证以下路径:

  1. 写入后立即读取。
  2. 重新启动应用后读取,确认数据持久化。
  3. 删除单个键后确认 containsKey 返回 false。
  4. deleteAll 后确认 readAll 返回空结果。
  5. 写入特殊字符、中文、空字符串和长文本。
  6. 模拟旧密文损坏,确认 resetOnError 的行为符合预期。

在这里插入图片描述

八、常见问题

读取返回 null

先确认写入时使用的 key 和读取 key 完全一致,再确认当前账号和应用实例没有切换。OHOS 实现会给 key 增加统一前缀,但业务层不需要感知前缀。

升级后旧数据无法解密

检查加密算法、密钥生命周期和首选项是否发生变化。不要直接清空用户数据,应先增加版本化迁移逻辑。

监听器没有收到变化

确认监听的是事件通道,并且写入、删除操作返回成功。事件通知只针对受监控 key,业务层应在订阅前先执行一次 readAll() 初始化列表。

九、社区与交流

反馈安全存储问题时请提供 Flutter 版本、OHOS SDK、设备信息、OhosOptions 配置、复现步骤和日志,避免在日志中直接粘贴真实密钥或用户凭证。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐