HarmonyOS鸿蒙App 增加本地密码哈希加密能力,支持三种 SHA 算法与三种编码格式,用一个组件实时生成高强度可记忆口令零权限开箱即用 —— hash_password 鸿蒙使用实战指南
开发工具: 华为云码道
本文配套仓库: 上游 WaterHashira/encrypt_password;OHOS 适配位于本地仓库提交
60c35a8的ohos/、example/ohos/、README.OpenHarmony_CN.md、README.OpenHarmony.md、CHANGELOG.OpenHarmony.md与docs/,真机截图与日志证据归档在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 首次读取 | 弹出剪贴板权限授权框,允许后成功读取(本次使用允许 / 始终允许 / 不允许) |
以下是操作的视屏,可以参考一下:
图一:demo 应用在 OpenHarmony 真机启动,展示平台版本卡片、密码输入、算法/编码选择、生成按钮
图二:输入密码后点击"生成加密密码",界面展示 SHA-256 + Hex + 16 位截断的加密结果
图三:Password_Hasher 组件包裹的输入框,输入时实时生成哈希并回填到输入框中
检查要点:
- 密码哈希的核心逻辑(SHA 哈希、编码转换、长度截断)全部为纯 Dart 实现,通过
crypto包和convert包完成,不走插件自己的"hash_password"MethodChannel; - 鸿蒙侧的 ArkTS 插件类
HashPasswordPlugin仅复刻了 Android/iOS 的模板契约(通道名hash_password、方法getPlatformVersion),保证插件注册链路完整; - 插件仅通过
@ohos.deviceInfo读取系统版本信息,不申请任何敏感权限,无需在module.json5中声明权限; - 完整实测过程见"六、运行与验证"。
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定位就是后者——将用户输入转换为高强度且易记的加密口令。
几个对使用者友好的特点:
- 零权限:哈希运算全部在 Dart 虚拟机内完成,不需要在
module.json5中申请任何权限; - 纯 Dart 核心:SHA 哈希、编码转换、长度截断全部通过
crypto+convert+fast_base58三个纯 Dart 包完成,不依赖任何原生平台 API; - 两种使用方式:既可通过
Hashing_Functionalities类手动调用三步哈希流程,也可通过Password_Hasher组件包裹输入框实现实时哈希; - 多算法多编码:支持 SHA-256 / SHA-384 / SHA-512 三种哈希强度,Hex / Base64 / Base58 三种编码输出,满足不同场景需求;
- 跨平台覆盖广:支持 Android、iOS 和鸿蒙三端,同一套 Dart 代码在各端行为一致。
接口说明:
| 名称 | 描述 | 类型 | 参数类型 | 返回值 | 必填 | 鸿蒙平台支持 |
|---|---|---|---|---|---|---|
HashPassword.platformVersion | 获取当前平台系统版本 | 属性 | 无 | Future<String?> | 否 | 是 |
Hashing_Functionalities.input_hash | 按指定算法对文本进行 SHA 哈希 | 方法 | String userText, String userSha | String | 是 | 是 |
Hashing_Functionalities.number_system_convert | 将哈希结果转换为指定编码 | 方法 | String userNumSys, String convHashText | String | 是 | 是 |
Hashing_Functionalities.final_encrypted_password | 按长度上限截断输出口令 | 方法 | String convertedText, double outputDigits | String | 是 | 是 |
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 编码则更紧凑且不含易混淆字符(0、O、I、l),适合人工输入和记忆。
三、环境准备
本文所有实测均在以下环境完成:
| 项 | 版本 | 说明 |
|---|---|---|
| Flutter(ohos 版) | 0.0.3-ohos-1.0.0-beta.1 | 主验证环境,真机实测 |
| DevEco Studio | 26.0.0 | 构建与签名 |
| 编译 SDK | OpenHarmony 5.1.0(18) | 宿主工程 compatibleSdkVersion 同值 |
| 真机 | OpenHarmony 6.1.1.120 | API 24,arm64,设备 ID 4UQ9K25508013016 |
两点提醒:
- 编译鸿蒙目标需要使用 ohos 版 Flutter SDK,普通 Flutter SDK 无法编译 ohos 产物;
- 本插件仅通过
@ohos.deviceInfo读取系统版本信息,不申请任何敏感权限,无需额外权限配置。
HarmonyOS 技术点:
@ohos.deviceInfo与displayVersion
@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.44 | 0.0.3-ohos-1.0.0-beta.1 | main |
说明:该 TAG 已在 3.44.9+ohos-0.0.1-canary1 真机上实测通过。原库的 Dart 层 API 与上游完全一致,适配过程对 Dart 代码零改动。
pubspec.yaml 中的 ohos 平台声明
适配后的
pubspec.yaml在flutter.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 个易混淆字符(0、O、I、l、+、/),适合人工输入和记忆。
三种编码的长度对比:以 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_base58) | getPlatformVersion | 无 | 无 |
| iOS | 纯 Dart(crypto 包) | 纯 Dart(convert + fast_base58) | getPlatformVersion | 无 | 无 |
| OpenHarmony | 纯 Dart(crypto 包) | 纯 Dart(convert + fast_base58) | getPlatformVersion | 无 | 无 |
鸿蒙侧的 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 真机 |
| 设备 ID | 4UQ9K25508013016 |
| 系统版本 | OpenHarmony 6.1.1.120 |
| API 版本 | 24 |
| 架构 | arm64 |
HarmonyOS 技术点:FlutterAbility 与 EntryAbility
鸿蒙 Flutter 应用的入口 Ability 需要继承
FlutterAbility(由@ohos/flutter_ohos提供),而非标准的UIAbility。FlutterAbility内部封装了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 层零改动 | 通过 |
以下是操作的视屏,可以参考一下:
七、工作原理
整个调用链路如下:
哈希运算(纯 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包 vshash_password通道hash_password 库涉及两条完全不同的路径,它们的职责和归属也不同:
路径 归属 职责 Dart 层是否调用 crypto包(纯 Dart 库)第三方 Dart 包 SHA-256/384/512 哈希运算 是( sha256.convert())hash_passwordMethodChannel插件自定义 仅 getPlatformVersion(模板骨架)是(但非核心功能) 密码哈希的核心功能走的是
crypto包——这是一个纯 Dart 加密库,不涉及任何 MethodChannel 或原生平台 API。SHA 运算全部在 Dart 虚拟机内完成。插件的hash_passwordMethodChannel 仅用于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_hash、number_system_convert、final_encrypted_password),对应哈希流程的三个步骤。详细代码解析见"五、代码接入"中的 5.2 节。
password_hasher.dart:组件封装
Password_Hasher 是一个 StatefulWidget,它包裹 TextFormField 并实现实时哈希。核心逻辑在 _Password_HasherState 的 hashing_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/catch try/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 编码排除了易混淆字符(0、O、I、l)和特殊符号,适合人工输入和记忆(如比特币地址就用 Base58Check 编码)。如果只是程序内部使用,Hex 或 Base64 均可;如果需要用户手动输入,选 Base58。
Q5:getPlatformVersion 返回的字符串格式是什么?
鸿蒙侧的 HashPasswordPlugin 在 getPlatformVersion 方法中返回 "OpenHarmony " + deviceInfo.displayVersion。deviceInfo 来自 @ohos.deviceInfo 模块,displayVersion 是系统的显示版本号(如 OpenHarmony-6.1.1.120)。最终返回值类似 "OpenHarmony OpenHarmony-6.1.1.120",前缀 OpenHarmony 是插件代码拼接的,后缀 OpenHarmony-6.1.1.120 是 displayVersion 的值。这一返回值与 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_hash → number_system_convert → final_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_passwordMethodChannel插件自定义 仅 getPlatformVersion是(模板骨架)
平台 哈希实现 编码转换 原生通道 权限 Dart 层改动 Android 纯 Dart( crypto)纯 Dart getPlatformVersion无 无 iOS 纯 Dart( crypto)纯 Dart getPlatformVersion无 无 OHOS 纯 Dart( crypto)纯 Dart getPlatformVersion无 无 核心要点:三步哈希流程(SHA → 编码 → 截断),纯 Dart 实现,零权限,两种使用方式(API 调用 + 组件包裹),三端行为一致,Dart 层零改动。
使用中发现任何问题,欢迎到配套仓库提 Issue,也欢迎发 PR 共建。
相关链接
欢迎加入 CPF-Flutter 鸿蒙社区,社区入口、环境搭建指南和本文相关链接统一放在这里:
更多推荐




所有评论(0)