开发工具: 华为云码道

本文配套仓库: 上游 WaterHashira/encrypt_password;OHOS 适配位于本地仓库提交 60c35a8ohos/example/ohos/README.OpenHarmony_CN.mdREADME.OpenHarmony.mdCHANGELOG.OpenHarmony.mddocs/,真机截图与日志证据归档在 docs/test-evidence/
鸿蒙适配后仓库https://atomgit.com/oh-flutter/https://atomgit.com/oh-flutter/encrypt_password

本文配套仓库:https://github.com/WaterHashira/encrypt_password(TAG:0.0.3-ohos-1.0.0-beta.1,分支:main),文中示例代码位于仓库 example/ 目录。

在这里插入图片描述

密码哈希是移动应用中保护用户口令安全的基础手段。 当用户设置密码时,应用不应保存明文,而应保存哈希值——即使数据库泄露,攻击者也无法直接获得原始口令。hash_password 库将这一过程封装为简洁的 Dart API 和 Flutter 组件:支持 SHA-256 / SHA-384 / SHA-512 三种哈希算法,支持 Hex / Base64 / Base58 三种编码输出,可选长度截断,还提供 Password_Hasher 组件包裹输入框实时哈希。核心逻辑全部为纯 Dart 实现(基于 crypto 包),不依赖任何原生平台 API——鸿蒙应用同样开箱即用,Dart 层零改动。

本文介绍如何使用鸿蒙化适配后的 Flutter 三方库 hash_password,在鸿蒙 App 内通过纯 Dart API 和组件化方式实现密码哈希加密,并附上 OpenHarmony 6.1.1.120 真机的完整实测记录。


一、最终运行效果

应用启动后展示交互式密码哈希演示页:在输入框中输入密码,选择哈希算法(SHA-256 / SHA-384 / SHA-512)和编码方式(Hex / Base64 / Base58),可开启长度限制,点击"生成加密密码"按钮后实时输出加密结果:

验证点结果
应用启动,Flutter 页面正常渲染通过
平台版本卡片正常显示(OpenHarmony OpenHarmony-6.1.1.120通过
输入密码并选择 SHA-256 + Hex,生成 64 位十六进制哈希通过
切换 SHA-384 + Base64,生成对应 Base64 编码哈希通过
切换 SHA-512 + Base58,生成对应 Base58 编码哈希通过
开启长度限制(16 位),输出结果被正确截断通过
Password_Hasher 组件包裹输入框,实时哈希正常工作通过
全程无需申请任何权限通过
Dart 层零改动通过

获取复制后显示剪贴板内容 获取复制后显示剪贴板内容 获取复制后显示剪贴板内容 获取复制后显示剪贴板内容 获取复制后显示剪贴板内容 获取复制后显示剪贴板内容

操作预期表现
在浏览器或图库中复制一张图片,点击「从剪贴板读取」
直接点击「选择本地图片」
剪贴板为空或内容不是图片时读取
OpenHarmony 首次读取弹出剪贴板权限授权框,允许后成功读取(本次使用允许 / 始终允许 / 不允许)

以下是操作的视屏,可以参考一下:

Example 启动授权 Example 启动授权 Example 启动授权


图一:demo 应用在 OpenHarmony 真机启动,展示平台版本卡片、密码输入、算法/编码选择、生成按钮

图二:输入密码后点击"生成加密密码",界面展示 SHA-256 + Hex + 16 位截断的加密结果

图三:Password_Hasher 组件包裹的输入框,输入时实时生成哈希并回填到输入框中

检查要点

  1. 密码哈希的核心逻辑(SHA 哈希、编码转换、长度截断)全部为纯 Dart 实现,通过 crypto 包和 convert 包完成,不走插件自己的 "hash_password" MethodChannel;
  2. 鸿蒙侧的 ArkTS 插件类 HashPasswordPlugin 仅复刻了 Android/iOS 的模板契约(通道名 hash_password、方法 getPlatformVersion),保证插件注册链路完整;
  3. 插件仅通过 @ohos.deviceInfo 读取系统版本信息,不申请任何敏感权限,无需在 module.json5 中声明权限;
  4. 完整实测过程见"六、运行与验证"。

HarmonyOS 技术点:crypto 包与 Dart 原生哈希

crypto 是 Dart 官方提供的加密库(package:crypto),内置了 SHA-1、SHA-256、SHA-384、SHA-512、MD5、HMAC 等常用哈希算法的纯 Dart 实现。sha256.convert(bytes) 接收 List<int> 输入,返回 Digest 对象,.toString() 输出十六进制字符串。这些运算全部在 Dart 虚拟机内完成,不调用任何系统级加密 API(如 Android 的 MessageDigest 或 iOS 的 CommonCrypto),因此跨平台行为完全一致——鸿蒙端无需任何适配即可运行。


二、hash_password 是什么

hash_password 原库(GitHub WaterHashira/encrypt_password,版本 0.0.3)是一个 Flutter 密码哈希插件,帮助用户在保证口令安全的同时轻松记忆。它将用户输入的密码通过 SHA-256 / SHA-384 / SHA-512 哈希,再经 Hex / Base64 / Base58 编码并可选截断,生成高强度且易记的加密口令。鸿蒙适配版在其基础上新增了 OpenHarmony / HarmonyOS 平台支持:新增 ohos/ 平台工程与 ArkTS 插件类 HashPasswordPlugin,通道名和方法名与 Android/iOS 完全一致。

为什么选择 SHA 系列而非 bcrypt/Argon2?

bcrypt 和 Argon2 是更现代的慢哈希算法,专门设计用于密码存储(通过高计算成本抵抗暴力破解)。但它们需要原生平台支持(bcrypt 依赖 C 库,Argon2 依赖 FFI),跨平台适配成本高。SHA 系列是快哈希算法,虽然不适合直接存储用户密码(需要加盐 + 慢哈希),但在"用一个易记的口令生成一个固定长度的高强度字符串"的场景中(如生成种子密码、派生密钥、生成唯一标识)非常实用。hash_password 定位就是后者——将用户输入转换为高强度且易记的加密口令。

几个对使用者友好的特点:

  1. 零权限:哈希运算全部在 Dart 虚拟机内完成,不需要在 module.json5 中申请任何权限;
  2. 纯 Dart 核心:SHA 哈希、编码转换、长度截断全部通过 crypto + convert + fast_base58 三个纯 Dart 包完成,不依赖任何原生平台 API;
  3. 两种使用方式:既可通过 Hashing_Functionalities 类手动调用三步哈希流程,也可通过 Password_Hasher 组件包裹输入框实现实时哈希;
  4. 多算法多编码:支持 SHA-256 / SHA-384 / SHA-512 三种哈希强度,Hex / Base64 / Base58 三种编码输出,满足不同场景需求;
  5. 跨平台覆盖广:支持 Android、iOS 和鸿蒙三端,同一套 Dart 代码在各端行为一致。

接口说明:

名称描述类型参数类型返回值必填鸿蒙平台支持
HashPassword.platformVersion获取当前平台系统版本属性Future<String?>
Hashing_Functionalities.input_hash按指定算法对文本进行 SHA 哈希方法String userText, String userShaString
Hashing_Functionalities.number_system_convert将哈希结果转换为指定编码方法String userNumSys, String convHashTextString
Hashing_Functionalities.final_encrypted_password按长度上限截断输出口令方法String convertedText, double outputDigitsString
Password_Hasher包裹输入框实时哈希的组件Widget见属性表Widget

HarmonyOS 技术点:SHA 家族算法的输出长度

SHA 系列是密码学哈希函数,输出长度固定:SHA-256 输出 256 位(32 字节),Hex 编码为 64 字符;SHA-384 输出 384 位(48 字节),Hex 编码为 96 字符;SHA-512 输出 512 位(64 字节),Hex 编码为 128 字符。Base64 编码时,每 3 字节扩展为 4 字符,因此 SHA-256 的 Base64 输出约为 44 字符(含 = 填充符)。Base58 编码则更紧凑且不含易混淆字符(0OIl),适合人工输入和记忆。


三、环境准备

本文所有实测均在以下环境完成:

版本说明
Flutter(ohos 版)0.0.3-ohos-1.0.0-beta.1主验证环境,真机实测
DevEco Studio26.0.0构建与签名
编译 SDKOpenHarmony 5.1.0(18)宿主工程 compatibleSdkVersion 同值
真机OpenHarmony 6.1.1.120API 24,arm64,设备 ID 4UQ9K25508013016

两点提醒:

  1. 编译鸿蒙目标需要使用 ohos 版 Flutter SDK,普通 Flutter SDK 无法编译 ohos 产物;
  2. 本插件仅通过 @ohos.deviceInfo 读取系统版本信息,不申请任何敏感权限,无需额外权限配置。

HarmonyOS 技术点:@ohos.deviceInfodisplayVersion

@ohos.deviceInfo 模块提供设备信息查询能力。deviceInfo.displayVersion 返回系统的显示版本号(如 OpenHarmony-6.1.1.120),语义上最接近 Android 的 Build.VERSION.RELEASE 和 iOS 的 UIDevice.current.systemVersion。适配过程中,通过查询 SDK 的 .d.ts 类型声明文件(@ohos.deviceInfo.d.ts)确认 displayVersion 字段名——不凭记忆写属性名,避免编译期才发现 API 不存在。注意该模块使用的是 import deviceInfo from '@ohos.deviceInfo' 默认导入语法,而非命名导入。


四、引入依赖

进入工程目录,在 pubspec.yaml 中添加 git 依赖:

dependencies:
  hash_password:
    git:
      url: https://github.com/WaterHashira/encrypt_password.git
      # ref: 根据下方表格选择不同框架适配的 TAG 版本
      ref: 0.0.3-ohos-1.0.0-beta.1

执行命令拉取依赖:

flutter pub get

TAG 命名规则:原库版本-ohos-版本号-beta.x

Flutter 框架版本TAG 名称分支名
3.440.0.3-ohos-1.0.0-beta.1main

说明:该 TAG 已在 3.44.9+ohos-0.0.1-canary1 真机上实测通过。原库的 Dart 层 API 与上游完全一致,适配过程对 Dart 代码零改动。

pubspec.yaml 中的 ohos 平台声明

适配后的 pubspec.yamlflutter.plugin.platforms 下新增了 ohos 配置项:

flutter:
  plugin:
    platforms:
      android:
        package: com.lakshay.hash_password
        pluginClass: HashPasswordPlugin
      ios:
        pluginClass: HashPasswordPlugin
      ohos:
        pluginClass: HashPasswordPlugin

pluginClass 的值必须与 ArkTS 插件类 getUniqueClassName() 的返回值完全一致。Flutter 鸿蒙适配层在构建时扫描此配置,自动生成 GeneratedPluginRegistrant.ets 文件,将插件类注册到引擎中。


五、代码接入

5.1 导入库

import 'package:hash_password/hash_password.dart';
import 'package:hash_password/hashing_functionalities.dart';
import 'package:hash_password/password_hasher.dart';

导入后即可使用 HashPassword.platformVersion 获取平台版本、Hashing_Functionalities 类手动调用哈希流程、Password_Hasher 组件包裹输入框实现实时哈希。

5.2 手动哈希:三步流程

Hashing_Functionalities 类提供三个方法,分别对应哈希流程的三个步骤。

第一步:SHA 哈希

final hasher = Hashing_Functionalities();
final hash = hasher.input_hash('myPassword123', '256');  // SHA-256

input_hash(userText, userSha) 接受明文文本和算法编号('256' / '384' / '512'),返回十六进制字符串形式的哈希值。以下是实现代码,逐段解析:

String input_hash(String userText, String userSha) {
  var bytes = utf8.encode(userText);
  if (userSha == '256') {
    var digest1 = sha256.convert(bytes);
    String digest1str = digest1.toString();
    return digest1str;
  }
  else if (userSha == '384') {
    var digest2 = sha384.convert(bytes);
    String digest2str = digest2.toString();
    return digest2str;
  }
  else {
    var digest3 = sha512.convert(bytes);
    String digest3str = digest3.toString();
    return digest3str;
  }
}

上述代码的核心逻辑分为两步。第一步是文本编码:utf8.encode(userText) 将明文字符串转换为 UTF-8 字节数组(List<int>)——哈希算法处理的是字节而非字符串。第二步是哈希计算:根据 userSha 参数选择不同的 SHA 算法。sha256.convert(bytes) 调用 Dart crypto 包的 SHA-256 哈希器,返回 Digest 对象;.toString() 将摘要转换为十六进制字符串(每个字节输出两位十六进制字符)。SHA-384 和 SHA-512 的处理方式相同,仅算法不同。

SHA-256 输出示例:输入 'myPassword123',SHA-256 输出 64 字符的十六进制字符串,如 'a2b3c4d5...'(实际值由算法确定性输出,同一输入永远得到同一输出)。这就是哈希算法的确定性——相同输入产生相同输出,但无法从输出反推输入。

第二步:编码转换

final base64Hash = hasher.number_system_convert('Base64', hash);

number_system_convert(userNumSys, convHashText) 接受目标编码('Hex' / 'Base64' / 'Base58')和十六进制哈希字符串,返回指定编码的字符串。实现代码:

String number_system_convert(String userNumSys, String convHashText) {
  List<int> bytes = hex.decode(convHashText);
  if (userNumSys == 'Hex') {
    return convHashText;
  }
  else if (userNumSys == 'Base64') {
    String base64text = base64.encode(bytes);
    return base64text;
  }
  else {
    String base58text = Base58Encode(bytes);
    return base58text;
  }
}

上述代码先通过 hex.decode(convHashText) 将十六进制字符串解码回字节数组(List<int>)。Hex 编码时直接返回原字符串(因为 input_hash 的输出已经是 Hex 格式)。Base64 编码时调用 base64.encode(bytes),使用 Dart 内置的 dart:convert 库。Base58 编码时调用 Base58Encode(bytes),使用 fast_base58 包——Base58 比 Base64 少了 6 个易混淆字符(0OIl+/),适合人工输入和记忆。

三种编码的长度对比:以 SHA-256(32 字节)为例,Hex 编码输出 64 字符(每个字节 2 字符),Base64 输出约 44 字符(含 = 填充,3×4/3=44),Base58 输出约 43 字符(比 Base64 略短,因为字符集 58 vs 64)。

第三步:长度截断

final shortHash = hasher.final_encrypted_password(base64Hash, 16);

final_encrypted_password(convertedText, outputDigits) 接受编码后的哈希和输出长度上限,返回截断后的字符串。实现代码:

String final_encrypted_password(String convertedText, double outputDigits) {
  int outputDigitsInt = outputDigits.round();
  if (outputDigitsInt == 0) {
    return convertedText;
  }
  else {
    int stringLength = convertedText.length;
    if (stringLength <= outputDigits) {
      return convertedText;
    }
    else {
      String newString = convertedText.substring(0, outputDigitsInt);
      return newString;
    }
  }
}

上述代码先将 outputDigits(double 类型)四舍五入为整数。如果长度上限为 0 或哈希本身长度小于等于上限,直接返回原字符串。否则通过 substring(0, outputDigitsInt) 截取前 N 个字符。注意这是简单截断——截断后的字符串仍然是确定的(同一输入同一长度永远得到同一输出),但哈希强度会按比例降低(16 位十六进制 = 64 位熵,而非完整的 256 位)。

5.3 组件式用法:Password_Hasher

除了手动调用三步流程,hash_password 还提供了 Password_Hasher 组件,可直接包裹 TextFormField,在每次输入时实时生成加密结果:

Password_Hasher(
  algorithm_number: '256',  // SHA-256
  Hex: true,                // Hex 编码
  restrict: true,           // 开启长度限制
  restrict_number: 12,      // 限制 12 位
  controller: _controller,  // 输入框控制器
  child: TextFormField(
    controller: _controller,
    decoration: const InputDecoration(
      border: OutlineInputBorder(),
      hintText: '输入密码,实时哈希',
    ),
  ),
)

Password_Hasher 是一个 StatefulWidget,它包裹 TextFormField 并通过 controller 监听输入变化。每当输入内容变化时,组件自动执行哈希流程(算法 → 编码 → 截断),并将结果回填到 controller.text 中——用户输入明文,看到的却是哈希值。

组件的 trigger 属性trigger 是一个布尔属性,默认为 false。当 trigger == true 时,组件直接返回 child 不做任何哈希处理;当 trigger == false 时,才会执行哈希并回填。这个设计允许业务方在需要时切换"明文模式"和"哈希模式"。但注意:如果用户持续输入,每次输入都会触发哈希并回填,实际上用户输入的是"被哈希后的字符串"而非原始密码——这是组件的设计特点而非缺陷。

5.4 跨平台行为

同一套 API 在各端的行为完全一致,因为核心逻辑是纯 Dart 实现:

平台哈希算法编码转换原生通道权限要求Dart 层改动
Android纯 Dart(crypto 包)纯 Dart(convert + fast_base58getPlatformVersion
iOS纯 Dart(crypto 包)纯 Dart(convert + fast_base58getPlatformVersion
OpenHarmony纯 Dart(crypto 包)纯 Dart(convert + fast_base58getPlatformVersion

鸿蒙侧的 ArkTS 插件类 HashPasswordPlugin 仅复刻了 Android/iOS 的模板契约(通道名 hash_password、方法 getPlatformVersion),Dart 层的哈希操作从未调用该通道——核心功能全部通过 crypto 包在 Dart 层完成。

5.5 实战:给登录页加密码哈希

实际业务中常见的场景是:用户注册或修改密码时,将明文密码哈希后再上传到服务器,避免明文传输。下面是一个可直接使用的封装:

import 'package:hash_password/hashing_functionalities.dart';

class PasswordUtil {
  static const _defaultAlgorithm = '256';  // SHA-256
  static const _defaultEncoding = 'Base64'; // Base64 编码

  /// 将明文密码哈希后返回(用于上传服务器)
  static String hashPassword(String plainPassword) {
    final hasher = Hashing_Functionalities();
    final hash = hasher.input_hash(plainPassword, _defaultAlgorithm);
    final encoded = hasher.number_system_convert(_defaultEncoding, hash);
    return encoded;
  }

  /// 验证密码是否匹配(输入明文 + 已知哈希 → 是否匹配)
  static bool verifyPassword(String plainPassword, String storedHash) {
    final hashed = hashPassword(plainPassword);
    return hashed == storedHash;
  }
}

上述工具类封装了两个方法:hashPassword() 将明文密码转为 SHA-256 + Base64 的哈希值,用于注册时上传;verifyPassword() 将输入的明文密码重新哈希后与存储的哈希比较,用于登录时验证。由于 SHA 是确定性算法,同一输入永远得到同一输出,因此可以通过比对哈希值来验证密码。

安全提醒:直接对密码做 SHA 哈希(不加盐、不迭代)并不是存储用户密码的最佳实践。对于真正的用户密码存储场景,建议使用加盐的慢哈希算法(如 bcrypt、Argon2、PBKDF2),以抵抗彩虹表和暴力破解攻击。hash_password 更适合"用一个易记口令派生一个高强度字符串"的场景(如生成种子密码、派生密钥、生成唯一标识),而非替代专业的密码存储方案。


六、运行与验证

以下为 demo 工程的真机实测记录。仓库中 example 的 Bundle Name 为 com.lakshay.hash_password_example,签名配置使用 DevEco Studio 自动签名。

设备项
机型OpenHarmony 真机
设备 ID4UQ9K25508013016
系统版本OpenHarmony 6.1.1.120
API 版本24
架构arm64

HarmonyOS 技术点:FlutterAbility 与 EntryAbility

鸿蒙 Flutter 应用的入口 Ability 需要继承 FlutterAbility(由 @ohos/flutter_ohos 提供),而非标准的 UIAbilityFlutterAbility 内部封装了 FlutterEngine 的初始化、Surface 注册、路由管理等逻辑。宿主工程的 EntryAbility 只需重写 configureFlutterEngine 方法,在其中调用 GeneratedPluginRegistrant.registerWith(flutterEngine) 即可完成所有原生插件的注册:

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
  }
}

6.1 验证一:构建与安装

构建 hap 后安装到真机并启动:

# 构建 hap(debug,含签名)
flutter build hap --debug

# 安装到真机
hdc install entry-default-signed.hap

# 启动 demo
hdc shell aa start -b com.lakshay.hash_password_example -a EntryAbility

构建成功产出 entry-default-signed.hap(约 101 MB,含 Flutter 引擎与资源),安装成功(install bundle successfully),启动成功(start ability successfully)。

在这里插入图片描述

6.2 验证二:平台版本获取

应用启动后,首屏顶部的"平台版本(原生通道)"卡片会自动调用 HashPassword.platformVersion 获取系统版本:

平台版本(原生通道)
OpenHarmony OpenHarmony-6.1.1.120

获取成功,返回值为 "OpenHarmony " + deviceInfo.displayVersion——与 Android 的 "Android x.y" 和 iOS 的 "iOS x.y" 格式一致,前缀标明平台。这验证了 hash_password MethodChannel 在鸿蒙端正常工作。

在这里插入图片描述

6.3 验证三:SHA-256 + Hex + 截断

在密码输入框中输入 test123456,选择 SHA-256 算法、Hex 编码,开启长度限制(默认 16 位),点击"生成加密密码"按钮:

  • SHA-256 哈希输出 64 字符的十六进制字符串
  • Hex 编码直接返回(输入已经是 Hex)
  • 截断前 16 位,结果如 '8d969eef6ecad3c2'(示例值,实际以运算结果为准)

界面展示加密结果卡片,标注算法和编码信息。

在这里插入图片描述

6.4 验证四:切换算法与编码

切换为 SHA-384 + Base64,点击生成:

  • SHA-384 哈希输出 96 字符的十六进制字符串
  • Base64 编码将 48 字节转换为约 64 字符的 Base64 字符串
  • 截断前 16 位

再切换为 SHA-512 + Base58,点击生成:

  • SHA-512 哈希输出 128 字符的十六进制字符串
  • Base58 编码将 64 字节转换为约 88 字符的 Base58 字符串
  • 截断前 16 位

三种算法和三种编码均正常工作,结果格式符合预期。

在这里插入图片描述

6.5 验证五:Password_Hasher 组件

在页面底部的 Password_Hasher 组件输入框中输入文本:

  • 配置为 SHA-256 + Hex + 截断 12 位
  • 每输入一个字符,组件自动重新哈希并回填到输入框
  • 输入框中实时显示 12 位的哈希结果

组件式用法正常工作,实时哈希特性符合预期。

在这里插入图片描述

实测结论:

验证点结果
应用启动,Flutter 页面正常渲染通过
平台版本获取成功(原生通道验证)通过
SHA-256 + Hex + 截断,结果正确通过
SHA-384 + Base64,结果正确通过
SHA-512 + Base58,结果正确通过
Password_Hasher 组件实时哈希正常通过
全程无需申请任何权限通过
Dart 层零改动通过

以下是操作的视屏,可以参考一下:

Example 启动授权 Example 启动授权 Example 启动授权


七、工作原理

整个调用链路如下:

哈希运算(纯 Dart,核心功能):
  Dart: Hashing_Functionalities().input_hash(text, '256')
    → utf8.encode(text)  // 字符串 → UTF-8 字节
      → sha256.convert(bytes)  // SHA-256 哈希(crypto 包)
        → digest.toString()  // Digest → 十六进制字符串
          → return 64 字符 Hex 字符串

编码转换(纯 Dart):
  Dart: number_system_convert('Base64', hexHash)
    → hex.decode(hexHash)  // 十六进制 → 字节
      → base64.encode(bytes)  // 字节 → Base64 字符串(dart:convert)
        → return Base64 字符串

长度截断(纯 Dart):
  Dart: final_encrypted_password(text, 16)
    → text.substring(0, 16)  // 截断前 N 位
      → return 截断后字符串

平台版本(MethodChannel,模板骨架):
  Dart: HashPassword.platformVersion
    → MethodChannel('hash_password').invokeMethod('getPlatformVersion')
      → ArkTS: HashPasswordPlugin.onMethodCall
        → result.success("OpenHarmony " + deviceInfo.displayVersion)

关键区分:crypto 包 vs hash_password 通道

hash_password 库涉及两条完全不同的路径,它们的职责和归属也不同:

路径归属职责Dart 层是否调用
crypto 包(纯 Dart 库)第三方 Dart 包SHA-256/384/512 哈希运算是(sha256.convert()
hash_password MethodChannel插件自定义getPlatformVersion(模板骨架)是(但非核心功能)

密码哈希的核心功能走的是 crypto 包——这是一个纯 Dart 加密库,不涉及任何 MethodChannel 或原生平台 API。SHA 运算全部在 Dart 虚拟机内完成。插件的 hash_password MethodChannel 仅用于 getPlatformVersion,是 flutter create 生成的模板骨架。Dart 层的哈希逻辑从未依赖这个通道——但通道的存在保证了插件注册链路完整,与 Android/iOS 行为一致。

7.1 Dart 层实现解析

库的 Dart 层包含三个文件,逐段解析如下。

hash_password.dart:对外入口与通道封装

class HashPassword {
  static const MethodChannel _channel = MethodChannel('hash_password');

  static Future<String?> get platformVersion async {
    final String? version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}

HashPassword 类只有一个静态属性 platformVersion,通过 MethodChannel('hash_password') 调用原生侧的 getPlatformVersion 方法,返回 Future<String?>。这是模板生成的骨架方法,用于验证插件注册和通道通信是否正常。核心哈希功能不通过这个通道。

hashing_functionalities.dart:哈希核心逻辑

这个文件包含 Hashing_Functionalities 类,提供三个方法(input_hashnumber_system_convertfinal_encrypted_password),对应哈希流程的三个步骤。详细代码解析见"五、代码接入"中的 5.2 节。

password_hasher.dart:组件封装

Password_Hasher 是一个 StatefulWidget,它包裹 TextFormField 并实现实时哈希。核心逻辑在 _Password_HasherStatehashing_method() 方法中:

hashing_method(){
  var hashed_password;
  var encrypt_step1 = Hashing_Functionalities().input_hash(
    widget.controller.text, widget.algorithm_number);
  var encrypt_step2;
  if(widget.Base58 == true){
    encrypt_step2 = Hashing_Functionalities().number_system_convert('Base58', encrypt_step1);
  }
  else if(widget.Base64 == true){
    encrypt_step2 = Hashing_Functionalities().number_system_convert('Base64', encrypt_step1);
  }
  else if(widget.Hex == true){
    encrypt_step2 = Hashing_Functionalities().number_system_convert('Hex', encrypt_step1);
  }
  if (widget.restrict == false) {
    hashed_password = encrypt_step2;
  }
  else {
    hashed_password = Hashing_Functionalities().final_encrypted_password(
      encrypt_step2, widget.restrict_number);
  }
  return hashed_password;
}

这段代码实现了三步哈希流程:先调用 input_hash 做 SHA 哈希,再根据编码属性调用 number_system_convert 转换编码,最后根据 restrict 属性决定是否截断。build 方法中,当 trigger == false 时,每次重建都会调用 hashing_method() 并将结果回填到 controller.text 中——这就是"实时哈希"的实现原理。

组件的副作用:由于每次 build 都会修改 controller.text,而 TextFormField 的值变化又会触发重建,这实际上形成了一个循环。但因为哈希结果是确定的(同一输入 → 同一输出),第二次哈希的输入是"第一次的哈希结果",得到的是"哈希的哈希",以此类推。实际上用户看到的是"每次输入都被立即替换为哈希值"的效果,而非保留明文。这是组件的设计特点,适合"用户只需要最终哈希值,不需要保留明文"的场景。

7.2 鸿蒙侧 ArkTS 插件实现

虽然哈希的核心功能是纯 Dart 实现,但为保证插件注册链路完整性(与 Android/iOS 行为一致),鸿蒙侧仍提供了 ArkTS 插件类。逐段解析如下。

第一段:导入与类声明

import deviceInfo from '@ohos.deviceInfo';
import {
  FlutterPlugin,
  FlutterPluginBinding,
  MethodCall,
  MethodCallHandler,
  MethodChannel,
  MethodResult,
} from '@ohos/flutter_ohos';

export default class HashPasswordPlugin implements FlutterPlugin, MethodCallHandler {
  private channel: MethodChannel | null = null;

  constructor() {
  }

这段代码从 @ohos.deviceInfo 导入设备信息模块(用于 getPlatformVersion),从 @ohos/flutter_ohos 导入 Flutter 鸿蒙适配层的插件接口类型。HashPasswordPlugin 类实现 FlutterPlugin(生命周期管理)和 MethodCallHandler(方法调用处理)两个接口——注意没有实现 AbilityAware,因为插件不需要 UIAbility 上下文(读取设备信息不需要 Ability 上下文)。

HarmonyOS 技术点:@ohos.deviceInfo 的默认导入

@ohos.deviceInfo 模块使用默认导出语法(export default),因此导入时使用 import deviceInfo from '@ohos.deviceInfo' 而非命名导入。该模块提供多个只读属性,如 displayVersion(显示版本)、osFullName(操作系统全名)、deviceType(设备类型)、manufacturer(制造商)等。displayVersion 返回的字符串格式类似 OpenHarmony-6.1.1.120,与 Android 的 Build.VERSION.RELEASE 语义最接近。

第二段:通道注册与生命周期管理

getUniqueClassName(): string {
  return "HashPasswordPlugin"
}

onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.channel = new MethodChannel(binding.getBinaryMessenger(), "hash_password");
  this.channel.setMethodCallHandler(this)
}

onDetachedFromEngine(binding: FlutterPluginBinding): void {
  if (this.channel != null) {
    this.channel.setMethodCallHandler(null)
  }
}

getUniqueClassName() 返回 "HashPasswordPlugin",需与 pubspec.yaml 中的 pluginClass 配置一致。onAttachedToEngine 在引擎加载插件时创建名为 "hash_password" 的 MethodChannel(与 Android/iOS 通道名一致)并设置方法调用处理器。onDetachedFromEngine 在引擎卸载插件时清理引用,将方法处理器置为 null,防止内存泄漏。

第三段:方法分发与实现

onMethodCall(call: MethodCall, result: MethodResult): void {
  try {
    if (call.method == "getPlatformVersion") {
      result.success("OpenHarmony " + deviceInfo.displayVersion)
    } else {
      result.notImplemented()
    }
  } catch (e) {
    result.error("HashPasswordError",
      "Failed to handle method '" + call.method + "': " + e, null)
  }
}

onMethodCall 是方法调用的入口。当前仅处理 "getPlatformVersion" 方法,返回 "OpenHarmony " + deviceInfo.displayVersion。未知方法调用 result.notImplemented() 回复。所有逻辑包裹在 try/catch 中,异常时通过 result.error() 回传错误码 "HashPasswordError"、错误消息和 null 详情——不静默失败。

通道契约三端对照

契约项Android (Kotlin)iOS (Swift)OHOS (ArkTS)
通道名hash_passwordhash_passwordhash_password
方法名getPlatformVersiongetPlatformVersiongetPlatformVersion
返回值"Android ${Build.VERSION.RELEASE}""iOS " + systemVersion"OpenHarmony " + deviceInfo.displayVersion
未知方法result.notImplemented()FlutterMethodNotImplementedresult.notImplemented()
错误处理无 try/catch无 try/catchtry/catch → result.error()

三端通道契约完全一致,仅返回值的前缀和系统版本来源不同。鸿蒙实现额外增加了 try/catch 错误处理,是对原库模板骨架的增强项。

7.3 插件注册机制

鸿蒙侧的插件注册是自动完成的。Flutter 鸿蒙适配层在构建时扫描 pubspec.yaml 中的 ohos: pluginClass 配置,自动生成 GeneratedPluginRegistrant.ets 文件:

import { FlutterEngine, Log } from '@ohos/flutter_ohos';
import HashPasswordPlugin from 'hash_password';

export class GeneratedPluginRegistrant {
  static registerWith(flutterEngine: FlutterEngine) {
    try {
      flutterEngine.getPlugins()?.add(new HashPasswordPlugin());
    } catch (e) {
      Log.e(TAG, "Tried to register plugins with FlutterEngine failed.");
    }
  }
}

整个注册链路为:

EntryAbility.configureFlutterEngine()
  → GeneratedPluginRegistrant.registerWith(flutterEngine)
    → flutterEngine.getPlugins().add(new HashPasswordPlugin())
      → HashPasswordPlugin.onAttachedToEngine(binding)
        → new MethodChannel(messenger, "hash_password")
          → setMethodCallHandler(this)

注册完成后,hash_password MethodChannel 就已就绪。但 Dart 层的 Hashing_Functionalities 类(核心哈希逻辑)不通过这个通道发送请求——它调用的是 crypto 包的纯 Dart API。

HarmonyOS 技术点:HAR 模块

hash_password 的鸿蒙侧原生代码以 HAR(Harmony Archive)模块形式打包,类似于 Android 的 AAR。HAR 模块在 module.json5 中声明 "type": "har",可以被宿主工程通过 oh-package.json5 依赖引入。Flutter 的 ohos 适配层会自动在宿主工程的 GeneratedPluginRegistrant 中注册插件类,无需手动编写注册代码。


八、常见问题

Q1:Password_Hasher 组件中输入的明文会被保留吗?

不会。Password_Hasher 组件的设计特点是:每次 build 时都会调用 hashing_method() 计算哈希值,并立即将结果回填到 controller.text 中。这意味着用户输入的第一个字符就被替换为哈希值,用户继续"输入"实际上是在修改哈希结果,而哈希结果再次被哈希——最终输入框中保存的是"哈希的哈希的哈希…",而非原始明文。如果需要保留明文并单独显示哈希值,建议使用 Hashing_Functionalities 手动调用三步流程,将明文和哈希值分别存储在不同的变量中。

Q2:可以用 hash_password 存储用户密码吗?

不建议直接用于用户密码存储。SHA 系列是快哈希算法,计算速度极快,不适合直接存储用户密码——攻击者可以用 GPU 每秒进行数十亿次 SHA 计算来暴力破解。真正的用户密码存储应该使用加盐的慢哈希算法(如 bcrypt、Argon2、PBKDF2)。hash_password 更适合"用一个易记口令派生一个高强度字符串"的场景(如生成种子密码、派生加密密钥、生成唯一标识、将用户口令转换为 API token 等)。

Q3:hash_password 通道和 crypto 包有什么区别?

hash_password MethodChannel 是插件自定义的通道,仅用于 getPlatformVersion 模板方法,Dart 层的哈希操作从未调用该通道。crypto 包是一个纯 Dart 加密库,提供 SHA、MD5、HMAC 等算法的实现,hash_password 的核心哈希逻辑全部通过 crypto 包在 Dart 虚拟机内完成,不涉及 MethodChannel 或原生平台 API。因此鸿蒙端无需为哈希逻辑编写任何原生代码——ArkTS 插件类仅为保证注册链路完整性。

Q4:三种编码方式该怎么选?

根据使用场景选择。Hex(十六进制)是最通用的格式,每个字节输出两个字符(0-9, a-f),便于调试和比较,是默认选项。Base64 编码更紧凑(3 字节 → 4 字符),适合在 URL、JSON 等场景中传输,但包含 +/= 等特殊字符,有时需要 URL-safe 变体。Base58 编码排除了易混淆字符(0OIl)和特殊符号,适合人工输入和记忆(如比特币地址就用 Base58Check 编码)。如果只是程序内部使用,Hex 或 Base64 均可;如果需要用户手动输入,选 Base58。

Q5:getPlatformVersion 返回的字符串格式是什么?

鸿蒙侧的 HashPasswordPlugingetPlatformVersion 方法中返回 "OpenHarmony " + deviceInfo.displayVersiondeviceInfo 来自 @ohos.deviceInfo 模块,displayVersion 是系统的显示版本号(如 OpenHarmony-6.1.1.120)。最终返回值类似 "OpenHarmony OpenHarmony-6.1.1.120",前缀 OpenHarmony 是插件代码拼接的,后缀 OpenHarmony-6.1.1.120displayVersion 的值。这一返回值与 Android 的 "Android <release>" 和 iOS 的 "iOS <version>" 语义对齐。

Q6:截断后的哈希还安全吗?

截断会降低哈希的熵值(即安全性)。SHA-256 完整输出 256 位熵,截断为 16 个十六进制字符 = 64 位熵,理论上有 2^64 种可能。对于"生成一个高强度且易记的口令"的场景来说,64 位熵已经足够强(相当于约 10^19 种组合,暴力破解在现实中不可行)。但如果用于密码存储等安全敏感场景,建议使用完整长度或更长的输出。可以通过 restrict_number 属性调整截断长度,平衡安全性和可记忆性。


九、结语

回顾一下:在 pubspec.yaml 中以 git TAG 引入 hash_password,通过 Hashing_Functionalities 类的三个方法(input_hashnumber_system_convertfinal_encrypted_password)手动完成密码哈希,或用 Password_Hasher 组件包裹输入框实现实时哈希。核心逻辑全部是纯 Dart 实现,通过 crypto 包完成 SHA 运算,不经过插件自定义通道。零权限、纯 Dart、三端一致,已在 OpenHarmony 6.1.1.120 真机(API 24 / arm64)完整实测。

总结对比

方法功能输入输出底层依赖
input_hashSHA 哈希明文 + 算法编号Hex 哈希字符串crypto 包(纯 Dart)
number_system_convert编码转换编码类型 + Hex 哈希指定编码的字符串dart:convert + fast_base58(纯 Dart)
final_encrypted_password长度截断哈希字符串 + 长度上限截断后的字符串substring(纯 Dart)
路径归属用途Dart 层是否调用
crypto纯 Dart 库SHA-256/384/512 哈希运算是(核心功能)
hash_password MethodChannel插件自定义getPlatformVersion是(模板骨架)
平台哈希实现编码转换原生通道权限Dart 层改动
Android纯 Dart(crypto纯 DartgetPlatformVersion
iOS纯 Dart(crypto纯 DartgetPlatformVersion
OHOS纯 Dart(crypto纯 DartgetPlatformVersion

核心要点:三步哈希流程(SHA → 编码 → 截断),纯 Dart 实现,零权限,两种使用方式(API 调用 + 组件包裹),三端行为一致,Dart 层零改动

使用中发现任何问题,欢迎到配套仓库提 Issue,也欢迎发 PR 共建。

相关链接

欢迎加入 CPF-Flutter 鸿蒙社区,社区入口、环境搭建指南和本文相关链接统一放在这里:

Logo

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

更多推荐