安全隐私 Cordova 与 OpenHarmony 混合开发实战
本文介绍了安全隐私功能的实现方案,包含Web端和OpenHarmony原生代码实现。Web端通过JavaScript实现密码设置、验证和SHA-256加密存储;OpenHarmony端采用原生插件方式提供密码管理接口,包括密码设置、验证和文件存储功能。两种方案都展示了如何在混合开发中保护用户数据安全,最终实现跨平台的安全隐私保护功能。

📌 模块概述
安全隐私功能是现代应用中不可或缺的重要组成部分,它负责保护用户的数据安全和个人隐私。在笔记应用中,用户可能会记录大量敏感信息,如工作计划、个人日记、财务记录等,这些数据的安全性直接关系到用户的隐私权益。因此,我们需要提供完善的安全保护机制。
本模块包含多个核心功能:密码保护功能允许用户设置应用密码,防止未经授权的访问;数据加密功能对敏感数据进行加密存储,即使数据文件被窃取也无法直接读取;隐私设置功能让用户可以自定义隐私保护级别,根据个人需求调整安全策略。这些功能共同构建了一个多层次的安全防护体系。
在实现安全功能时,我们需要遵循业界最佳实践。密码不应该以明文形式存储,而是使用单向哈希算法处理后再保存。加密算法应该选择经过验证的标准算法,如 AES、SHA-256 等。同时,还要考虑用户体验,避免过于复杂的安全设置影响正常使用。本文将详细介绍如何在 Web 端和 OpenHarmony 原生端实现这些安全功能。
🔗 完整流程
安全隐私功能的实现涉及多个环节,从用户设置密码到数据加密存储,每个步骤都需要精心设计和实现。
第一步:设置密码
密码设置是安全功能的入口。用户首次使用应用时,系统会引导用户设置一个安全密码。密码需要满足一定的强度要求,比如最少6位字符,建议包含字母、数字和特殊字符的组合。设置密码时,系统会对密码进行哈希处理,将哈希值存储到本地,而不是存储明文密码。
密码设置界面应该提供清晰的提示信息,告知用户密码强度要求和安全建议。可以实时显示密码强度指示器,帮助用户设置更安全的密码。同时,还应该提供密码可见性切换功能,方便用户确认输入的密码是否正确。设置成功后,系统会显示确认信息,并引导用户记住密码。
第二步:验证密码
当用户再次打开应用时,系统会显示密码验证界面,要求用户输入之前设置的密码。验证过程是将用户输入的密码进行哈希处理,然后与存储的哈希值进行比对。如果匹配成功,则允许用户进入应用;如果失败,则显示错误提示,并允许用户重新输入。
为了防止暴力破解,系统应该限制密码尝试次数。比如连续输入错误密码5次后,锁定应用一段时间。同时,可以提供"忘记密码"功能,通过其他验证方式(如邮箱验证、安全问题等)帮助用户重置密码。验证界面的设计要简洁明了,避免给用户造成困扰。
第三步:加密数据
数据加密是保护用户隐私的核心机制。当用户启用数据加密功能后,所有敏感数据在存储前都会经过加密处理。加密使用用户密码派生的密钥,确保只有知道密码的人才能解密数据。加密算法应该选择 AES-256 等强加密标准,确保数据安全性。
加密过程对用户是透明的,用户在使用应用时感觉不到加密的存在。当用户读取数据时,系统自动解密;保存数据时,系统自动加密。这种无感知的加密方式既保证了安全性,又不影响用户体验。同时,系统还应该提供数据备份和恢复功能,确保加密数据不会因为设备损坏而丢失。
🔧 Web代码实现
安全设置界面
安全设置界面是用户与安全功能交互的主要入口,需要提供清晰的布局和友好的交互体验。
class SecurityManager {
constructor() {
this.settings = null;
this.isLocked = true;
this.failedAttempts = 0;
this.maxAttempts = 5;
this.lockoutTime = 300000;
}
async init() {
this.settings = await noteDB.getSettings();
if (!this.settings.security) {
this.settings.security = {
passwordEnabled: false,
encryptionEnabled: false,
biometricEnabled: false
};
}
}
}
SecurityManager 类是安全功能的核心管理器,负责协调各种安全相关的操作。构造函数中初始化了关键属性:isLocked 表示应用是否处于锁定状态,failedAttempts 记录密码输入失败次数,maxAttempts 和 lockoutTime 定义了安全策略。init 方法从数据库加载安全设置,如果设置不存在则创建默认配置,确保应用首次运行时也能正常工作。
async renderSecurity() {
const hasPassword = this.settings.security.passwordEnabled;
const encryptionEnabled = this.settings.security.encryptionEnabled;
return `
<div class="page active">
<div class="page-header">
<h1 class="page-title">🔒 安全隐私</h1>
<p class="page-subtitle">保护你的数据安全</p>
</div>
renderSecurity 方法负责渲染安全设置页面。首先从设置对象中读取当前的安全状态,判断是否已设置密码和是否启用了加密。然后开始构建 HTML 结构,页面头部包含标题和副标题,为用户提供清晰的页面定位。使用模板字符串可以方便地嵌入变量和条件逻辑。
<div class="security-container">
<div class="security-section">
<h3 class="section-title">🔐 密码保护</h3>
<p class="section-description">设置应用密码,防止未经授权的访问</p>
${hasPassword ? `
<div class="password-status">
<span class="status-badge success">已设置</span>
<button class="btn btn-secondary" onclick="security.changePassword()">
修改密码
</button>
安全容器的第一个部分是密码保护设置。使用条件渲染根据是否已设置密码显示不同的界面。如果已设置密码,显示状态徽章和修改密码按钮;如果未设置,则显示密码输入表单。section-title 和 section-description 为用户提供功能说明,帮助用户理解每个设置项的作用。
<button class="btn btn-danger" onclick="security.removePassword()">
移除密码
</button>
</div>
` : `
<div class="form-group">
<label class="form-label">新密码</label>
<div class="password-input-wrapper">
<input type="password" id="new-password"
placeholder="至少6位字符"
class="form-control"
oninput="security.checkPasswordStrength(this.value)">
密码输入表单包含密码输入框和强度指示器。input 元素使用 oninput 事件实时检查密码强度,为用户提供即时反馈。password-input-wrapper 包装器用于添加密码可见性切换按钮。placeholder 提示用户密码的最低要求,帮助用户设置符合规范的密码。
<button type="button" class="btn-icon"
onclick="security.togglePasswordVisibility('new-password')">
👁️
</button>
</div>
<div class="password-strength" id="password-strength">
<div class="strength-bar"></div>
<span class="strength-text">密码强度:弱</span>
</div>
<label class="form-label">确认密码</label>
<input type="password" id="confirm-password"
密码可见性切换按钮使用 emoji 图标,点击后可以在明文和密文之间切换。密码强度指示器包含进度条和文字说明,实时显示密码的安全等级。确认密码输入框用于防止用户输入错误,确保用户准确记住设置的密码。这种双重输入的设计是密码设置的标准做法。
placeholder="再次输入密码"
class="form-control">
<button class="btn btn-primary" onclick="security.setPassword()">
设置密码
</button>
</div>
`}
</div>
<div class="security-section">
<h3 class="section-title">🔐 数据加密</h3>
设置密码按钮触发密码保存逻辑,会验证两次输入是否一致,检查密码强度是否符合要求。密码保护部分的条件渲染结束后,开始第二个安全部分:数据加密设置。数据加密功能允许用户选择是否对笔记内容进行加密存储,提供额外的安全保护层。
<p class="section-description">加密存储敏感数据,提升安全性</p>
<div class="form-group">
<label class="switch-label">
<input type="checkbox" id="encrypt-data"
${encryptionEnabled ? 'checked' : ''}
onchange="security.toggleEncryption(this.checked)">
<span class="switch-slider"></span>
<span class="switch-text">启用数据加密</span>
</label>
数据加密使用开关控件,用户可以通过点击开关来启用或禁用加密功能。checkbox 的 checked 属性根据当前设置动态设置,确保界面状态与实际设置一致。onchange 事件监听开关状态变化,实时更新加密设置。switch-slider 和 switch-text 配合 CSS 实现美观的开关效果。
${encryptionEnabled ? `
<div class="encryption-info">
<p class="info-text">
✅ 你的笔记数据已加密保护
</p>
<p class="info-text small">
加密算法:AES-256<br>
密钥派生:PBKDF2
</p>
</div>
` : `
<div class="encryption-warning">
当加密功能启用时,显示加密状态信息和使用的加密算法详情。这让用户了解数据是如何被保护的,增强用户对安全功能的信任。AES-256 是目前最安全的对称加密算法之一,PBKDF2 是标准的密钥派生函数,可以从用户密码生成强加密密钥。
<p class="warning-text">
⚠️ 数据未加密,建议启用加密保护
</p>
</div>
`}
</div>
</div>
</div>
</div>
`;
}
当加密功能未启用时,显示警告信息提醒用户数据存在安全风险,建议启用加密。这种提示性的设计可以引导用户采取更安全的做法。至此,安全设置界面的 HTML 结构构建完成,返回完整的页面字符串。整个界面采用卡片式布局,每个安全功能独立成一个区块,结构清晰易懂。
密码管理功能
密码管理是安全功能的核心,包括密码设置、验证、修改和移除等操作。
async setPassword() {
try {
const password = document.getElementById('new-password').value;
const confirmPassword = document.getElementById('confirm-password').value;
if (!password || password.length < 6) {
Utils.showToast('密码长度至少6位', 'error');
return;
}
if (password !== confirmPassword) {
Utils.showToast('两次输入的密码不一致', 'error');
setPassword 方法处理密码设置逻辑。首先从输入框获取用户输入的密码和确认密码,然后进行一系列验证。检查密码长度是否符合最低要求,这是最基本的安全策略。比对两次输入的密码是否一致,防止用户因输入错误而设置了错误的密码。每个验证失败都会显示相应的错误提示,帮助用户纠正输入。
return;
}
const strength = this.calculatePasswordStrength(password);
if (strength < 2) {
const confirmed = confirm('密码强度较弱,是否继续?');
if (!confirmed) return;
}
const hashedPassword = await this.hashPassword(password);
this.settings.security.password = hashedPassword;
计算密码强度并给出警告。如果密码强度低于中等水平,弹出确认对话框询问用户是否继续。这种设计既保证了安全性,又不强制用户必须使用复杂密码,在安全和易用性之间取得平衡。通过 hashPassword 方法对密码进行哈希处理,得到的哈希值保存到设置对象中。绝不存储明文密码是安全开发的基本原则。
this.settings.security.passwordEnabled = true;
await noteDB.updateSettings(this.settings);
Utils.showToast('密码已设置', 'success');
document.getElementById('new-password').value = '';
document.getElementById('confirm-password').value = '';
this.renderSecurity();
} catch (error) {
console.error('设置密码失败:', error);
Utils.showToast('操作失败,请重试', 'error');
设置密码启用标志,将更新后的设置保存到数据库。保存成功后显示成功提示,清空输入框,重新渲染页面以显示新的状态。这种即时反馈让用户清楚地知道操作已经完成。使用 try-catch 捕获可能出现的异常,如数据库写入失败、网络错误等,确保应用不会因为异常而崩溃。
}
}
async hashPassword(password) {
const encoder = new TextEncoder();
const data = encoder.encode(password);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
hashPassword 方法使用 Web Crypto API 对密码进行 SHA-256 哈希处理。首先将字符串密码编码为字节数组,然后使用 crypto.subtle.digest 计算哈希值。哈希结果是 ArrayBuffer 格式,需要转换为 Uint8Array,再转换为十六进制字符串。padStart 确保每个字节都是两位十六进制数,最后拼接成完整的哈希字符串。SHA-256 是单向哈希算法,无法从哈希值反推原始密码。
async verifyPassword(inputPassword) {
try {
if (!this.settings.security.passwordEnabled) {
return true;
}
const hashedInput = await this.hashPassword(inputPassword);
const storedHash = this.settings.security.password;
if (hashedInput === storedHash) {
this.failedAttempts = 0;
verifyPassword 方法用于验证用户输入的密码是否正确。首先检查是否启用了密码保护,如果没有启用则直接返回 true。对用户输入的密码进行哈希处理,然后与存储的哈希值进行比对。如果匹配成功,重置失败计数器,允许用户访问应用。这种验证方式确保了即使数据库被窃取,攻击者也无法直接获取用户的明文密码。
this.isLocked = false;
return true;
} else {
this.failedAttempts++;
if (this.failedAttempts >= this.maxAttempts) {
this.lockApp();
Utils.showToast(`密码错误次数过多,应用已锁定${this.lockoutTime / 60000}分钟`, 'error');
} else {
Utils.showToast(`密码错误,还可尝试${this.maxAttempts - this.failedAttempts}次`, 'error');
}
如果密码验证失败,增加失败计数器。检查失败次数是否达到上限,如果达到则锁定应用一段时间,防止暴力破解。显示剩余尝试次数,让用户知道还有多少机会。这种渐进式的安全策略既保护了应用安全,又给用户足够的容错空间。锁定时间可以根据安全需求调整,通常设置为5-30分钟。
return false;
}
} catch (error) {
console.error('验证密码失败:', error);
return false;
}
}
calculatePasswordStrength(password) {
let strength = 0;
if (password.length >= 8) strength++;
if (password.length >= 12) strength++;
验证失败返回 false,异常情况也返回 false,采用安全优先的策略。calculatePasswordStrength 方法计算密码强度,使用简单的评分系统。密码长度是强度的重要指标,8位以上加1分,12位以上再加1分。这种分级评分可以量化密码的安全程度,帮助用户设置更安全的密码。
if (/[a-z]/.test(password) && /[A-Z]/.test(password)) strength++;
if (/\d/.test(password)) strength++;
if (/[^a-zA-Z\d]/.test(password)) strength++;
return strength;
}
checkPasswordStrength(password) {
const strength = this.calculatePasswordStrength(password);
const strengthBar = document.querySelector('.strength-bar');
const strengthText = document.querySelector('.strength-text');
使用正则表达式检查密码是否包含大小写字母、数字和特殊字符,每满足一项加1分。最终强度分数在0-5之间,分数越高密码越安全。checkPasswordStrength 方法在用户输入密码时实时更新强度指示器,获取页面上的强度条和文字元素,准备更新它们的状态。
const levels = ['弱', '较弱', '中等', '较强', '强', '很强'];
const colors = ['#ff4444', '#ff8844', '#ffbb44', '#88cc44', '#44cc44', '#00cc44'];
strengthBar.style.width = `${(strength / 5) * 100}%`;
strengthBar.style.backgroundColor = colors[strength];
strengthText.textContent = `密码强度:${levels[strength]}`;
}
定义强度等级的文字描述和对应的颜色。根据强度分数设置进度条的宽度和颜色,更新文字说明。这种视觉反馈让用户直观地看到密码的安全程度,鼓励用户设置更强的密码。颜色从红色渐变到绿色,符合用户的直觉认知。
数据加密功能
数据加密功能使用对称加密算法保护用户的敏感数据,确保即使数据文件被窃取也无法直接读取。
async toggleEncryption(enabled) {
try {
if (enabled && !this.settings.security.passwordEnabled) {
Utils.showToast('请先设置密码', 'warning');
document.getElementById('encrypt-data').checked = false;
return;
}
if (enabled) {
const confirmed = confirm('启用加密后,所有笔记将被加密存储。确认继续?');
toggleEncryption 方法处理加密功能的开关。首先检查是否已设置密码,因为加密需要使用密码派生的密钥。如果没有密码就尝试启用加密,显示警告并阻止操作。如果启用加密,弹出确认对话框告知用户加密的影响,确保用户理解这个操作的含义。这种谨慎的设计可以避免用户误操作。
if (!confirmed) {
document.getElementById('encrypt-data').checked = false;
return;
}
await this.encryptAllNotes();
} else {
const confirmed = confirm('禁用加密后,笔记将以明文存储。确认继续?');
if (!confirmed) {
document.getElementById('encrypt-data').checked = true;
return;
如果用户取消确认,恢复开关状态并返回。如果确认启用,调用 encryptAllNotes 方法加密所有现有笔记。禁用加密时也需要确认,因为这会降低数据的安全性。如果用户取消,保持开关为启用状态。这种双向确认机制确保用户充分理解操作的后果。
}
await this.decryptAllNotes();
}
this.settings.security.encryptionEnabled = enabled;
await noteDB.updateSettings(this.settings);
Utils.showToast(enabled ? '加密已启用' : '加密已禁用', 'success');
this.renderSecurity();
} catch (error) {
如果确认禁用,调用 decryptAllNotes 方法解密所有笔记。更新加密启用标志,保存设置到数据库。显示操作结果提示,重新渲染页面以反映新状态。整个过程包含多个异步操作,使用 await 确保操作按顺序执行。异常处理确保即使某个步骤失败,应用也能正常运行。
console.error('切换加密失败:', error);
Utils.showToast('操作失败,请重试', 'error');
document.getElementById('encrypt-data').checked = !enabled;
}
}
async encryptAllNotes() {
const notes = await noteDB.getAllNotes();
const encryptedNotes = await Promise.all(
notes.map(note => this.encryptNote(note))
);
await noteDB.saveAllNotes(encryptedNotes);
异常处理中恢复开关状态,确保界面与实际状态一致。encryptAllNotes 方法批量加密所有笔记,首先获取所有笔记数据,然后使用 Promise.all 并行加密每一篇笔记,提高处理效率。加密完成后批量保存到数据库。这种批处理方式适合处理大量数据,比逐个处理更高效。
}
async encryptNote(note) {
if (note.encrypted) return note;
const key = await this.deriveKey(this.settings.security.password);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encoder = new TextEncoder();
const data = encoder.encode(note.content);
const encryptedData = await crypto.subtle.encrypt(
encryptNote 方法加密单篇笔记。首先检查笔记是否已加密,避免重复加密。从密码派生加密密钥,生成随机初始化向量(IV),将笔记内容编码为字节数组。使用 Web Crypto API 的 encrypt 方法进行 AES-GCM 加密,这是一种认证加密模式,既保证机密性又保证完整性。
{ name: 'AES-GCM', iv: iv },
key,
data
);
return {
...note,
content: this.arrayBufferToBase64(encryptedData),
iv: this.arrayBufferToBase64(iv),
encrypted: true
};
}
加密操作返回 ArrayBuffer 格式的密文,需要转换为 Base64 字符串以便存储。返回新的笔记对象,包含加密后的内容、IV 和加密标志。IV 需要保存下来,因为解密时需要使用相同的 IV。使用对象展开运算符保留笔记的其他属性,只替换内容相关的字段。这种不可变数据的处理方式更安全可靠。
🔌 OpenHarmony 原生代码
OpenHarmony 端需要实现与 Web 端对等的安全功能,包括密码管理和数据加密等核心能力。
安全插件基础架构
import { webview } from '@kit.ArkWeb';
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';
@NativeComponent
export class SecurityPlugin {
private context: common.UIAbilityContext;
private settingsPath: string;
private failedAttempts: number = 0;
private lockoutUntil: number = 0;
首先引入 OpenHarmony 开发所需的核心工具包。webview 用于与 Web 端通信,common 提供应用上下文,fileIo 负责文件操作,cryptoFramework 提供加密功能。通过 @NativeComponent 装饰器声明为原生组件。定义关键属性:context 存储应用上下文,settingsPath 存储设置文件路径,failedAttempts 和 lockoutUntil 用于实现防暴力破解机制。
constructor(context: common.UIAbilityContext) {
this.context = context;
this.settingsPath = this.context.filesDir + '/settings.json';
this.ensureSettingsFile();
}
private ensureSettingsFile(): void {
try {
if (!fileIo.accessSync(this.settingsPath)) {
const defaultSettings = {
security: {
构造函数接收应用上下文并初始化设置文件路径。调用 ensureSettingsFile 方法确保设置文件存在。使用 accessSync 同步检查文件是否存在,如果不存在则创建默认设置。这种初始化逻辑确保应用首次运行时也能正常工作,避免因文件不存在而出错。
passwordEnabled: false,
encryptionEnabled: false,
password: ''
}
};
fileIo.writeTextSync(this.settingsPath, JSON.stringify(defaultSettings, null, 2));
}
} catch (error) {
console.error('Failed to ensure settings file:', error);
}
}
默认设置对象包含安全相关的配置项,所有安全功能默认关闭。使用 writeTextSync 同步写入文件,JSON.stringify 的第三个参数设置缩进为2,使文件内容更易读。添加 try-catch 异常处理,避免因权限问题或磁盘空间不足导致应用崩溃。这种防御性编程提高了应用的健壮性。
public init(webviewController: webview.WebviewController): void {
webviewController.registerJavaScriptProxy(
new SecurityJSProxy(this),
'securityPlugin',
['setPassword', 'verifyPassword', 'changePassword', 'removePassword', 'encryptData', 'decryptData']
);
}
init 方法注册 JavaScript 代理对象,将原生功能暴露给 Web 端。方法白名单包含所有安全相关的操作:设置密码、验证密码、修改密码、移除密码、加密数据、解密数据。这种显式声明的方式提高了安全性,只有列出的方法才能被 Web 端调用。
密码管理实现
public setPassword(password: string): Promise<boolean> {
return new Promise((resolve) => {
try {
if (password.length < 6) {
resolve(false);
return;
}
const settings = this.loadSettings();
settings.security.password = this.hashPassword(password);
settings.security.passwordEnabled = true;
setPassword 方法处理密码设置逻辑。返回 Promise 对象以支持异步操作。首先验证密码长度,不符合要求直接返回 false。从文件加载当前设置,对密码进行哈希处理后保存,同时设置密码启用标志。这种验证-处理-保存的流程确保了数据的一致性和安全性。
this.saveSettings(settings);
resolve(true);
} catch (error) {
console.error('Failed to set password:', error);
resolve(false);
}
});
}
public verifyPassword(password: string): Promise<boolean> {
return new Promise((resolve) => {
try {
保存更新后的设置,返回 true 表示成功。异常情况返回 false,确保调用方能正确处理错误。verifyPassword 方法验证用户输入的密码,同样返回 Promise 对象。使用 try-catch 包裹所有操作,确保任何异常都能被捕获和处理,避免应用崩溃。
const now = Date.now();
if (this.lockoutUntil > now) {
resolve(false);
return;
}
const settings = this.loadSettings();
if (!settings.security.passwordEnabled) {
resolve(true);
return;
}
首先检查应用是否处于锁定状态,如果当前时间小于锁定截止时间,直接返回 false 拒绝验证。加载设置并检查是否启用了密码保护,如果没有启用则返回 true 允许访问。这种多层检查确保了安全策略的正确执行,防止绕过安全机制。
const hashedInput = this.hashPassword(password);
const storedHash = settings.security.password;
if (hashedInput === storedHash) {
this.failedAttempts = 0;
resolve(true);
} else {
this.failedAttempts++;
if (this.failedAttempts >= 5) {
this.lockoutUntil = now + 300000;
}
对输入密码进行哈希处理并与存储的哈希值比对。如果匹配成功,重置失败计数器并返回 true。如果失败,增加失败计数器,达到5次后设置锁定截止时间为5分钟后。这种防暴力破解机制有效保护了用户账户安全,即使攻击者获得了设备访问权限,也很难通过暴力破解获取密码。
resolve(false);
}
} catch (error) {
console.error('Failed to verify password:', error);
resolve(false);
}
});
}
private hashPassword(password: string): string {
const encoder = new util.TextEncoder();
const data = encoder.encodeInto(password);
验证失败返回 false,异常情况也返回 false,采用安全优先的策略。hashPassword 方法使用 OpenHarmony 的工具类对密码进行哈希处理。首先将字符串编码为字节数组,这是进行哈希运算的前提。在实际生产环境中,应该使用 cryptoFramework 提供的标准哈希算法,这里为了演示简化了实现。
let hash = 0;
for (let i = 0; i < data.length; i++) {
hash = ((hash << 5) - hash) + data[i];
hash = hash & hash;
}
return hash.toString(16);
}
private loadSettings(): any {
try {
const content = fileIo.readTextSync(this.settingsPath);
这里使用简单的哈希算法进行演示,实际应用中应该使用 SHA-256 等标准算法。通过位运算和循环计算哈希值,最后转换为十六进制字符串。loadSettings 方法从文件系统读取设置数据,使用 readTextSync 同步读取文件内容,然后解析为 JavaScript 对象。
return JSON.parse(content);
} catch (error) {
console.error('Failed to load settings:', error);
return { security: { passwordEnabled: false, encryptionEnabled: false, password: '' } };
}
}
private saveSettings(settings: any): void {
try {
fileIo.writeTextSync(this.settingsPath, JSON.stringify(settings, null, 2));
} catch (error) {
JSON.parse 将字符串解析为对象。如果读取或解析失败,返回默认设置对象,确保应用能继续运行。saveSettings 方法将设置对象保存到文件,使用 JSON.stringify 序列化对象,writeTextSync 同步写入文件。异常处理确保即使保存失败也不会导致应用崩溃。
JavaScript 代理类
console.error('Failed to save settings:', error);
}
}
}
class SecurityJSProxy {
private plugin: SecurityPlugin;
constructor(plugin: SecurityPlugin) {
this.plugin = plugin;
}
SecurityJSProxy 类是 Web 端与原生端通信的桥梁。构造函数接收 SecurityPlugin 实例并保存,确保方法调用时上下文正确。这个代理类将原生方法暴露给 Web 端,使 Web 端可以通过 JavaScript 调用原生功能。代理模式的使用使得接口更加清晰,职责分离更加明确。
setPassword(password: string): void {
this.plugin.setPassword(password).then(success => {
console.log('Password set:', success);
this.sendMessageToWeb('passwordSet', { success });
}).catch(error => {
console.error('Failed to set password:', error);
this.sendMessageToWeb('passwordSetError', { message: error.message });
});
}
verifyPassword(password: string): void {
setPassword 方法调用原生插件的同名方法,使用 then 捕获结果并通过 sendMessageToWeb 发送给 Web 端。catch 处理异常情况,确保错误信息也能传递给 Web 端。这种异步通信机制使得 Web 端可以实时获取原生操作的结果,实现流畅的用户体验。
this.plugin.verifyPassword(password).then(success => {
console.log('Password verified:', success);
this.sendMessageToWeb('passwordVerified', { success });
}).catch(error => {
console.error('Failed to verify password:', error);
this.sendMessageToWeb('passwordVerifyError', { message: error.message });
});
}
private sendMessageToWeb(event: string, data: any): void {
const message = JSON.stringify({ event, data });
verifyPassword 方法的实现与 setPassword 类似,调用原生方法并将结果发送给 Web 端。sendMessageToWeb 方法负责向 Web 端发送消息,将事件名和数据封装为 JSON 字符串。这种统一的消息格式使得 Web 端可以方便地处理各种原生事件。
webview.runJavaScript(
`window.dispatchEvent(new CustomEvent('${event}', { detail: ${message} }))`
);
}
}
使用 webview.runJavaScript 在 Web 端执行 JavaScript 代码,触发自定义事件。Web 端可以通过监听这些事件来接收原生端的数据和状态更新。CustomEvent 的 detail 属性包含消息数据,Web 端可以从中提取需要的信息。这种事件驱动的通信方式实现了松耦合的架构,便于维护和扩展。
📝 总结
安全隐私功能是现代应用的重要组成部分,直接关系到用户数据的安全和隐私保护。本文详细介绍了如何在 Web 端和 OpenHarmony 原生端实现完整的安全功能,包括密码保护、密码验证、数据加密等核心能力。
Web 端使用 Web Crypto API 提供的标准加密算法,实现了密码哈希、数据加密等功能。通过 SHA-256 哈希算法保护密码,使用 AES-GCM 加密算法保护数据内容。界面设计注重用户体验,提供密码强度指示、实时验证等友好功能。同时实现了防暴力破解机制,限制密码尝试次数,提高了安全性。
OpenHarmony 原生端实现了与 Web 端对等的功能,通过文件系统存储设置数据,使用 JavaScript 代理类实现与 Web 端的通信。原生端的实现考虑了平台特性,使用了 OpenHarmony 提供的标准 API,确保了代码的可移植性和可维护性。
在实际开发中,还需要考虑更多的安全细节。比如使用更强的密钥派生函数(如 PBKDF2、Argon2),增加密码破解的难度;实现生物识别认证(指纹、面部识别),提供更便捷的安全验证方式;添加数据备份和恢复功能,防止因设备损坏导致数据丢失;实现安全日志记录,追踪安全相关的操作和异常。
安全功能的实现需要在安全性和易用性之间取得平衡。过于复杂的安全设置会影响用户体验,而过于简单的安全措施又无法有效保护数据。开发者需要根据应用的实际需求和用户群体特点,设计合适的安全策略。同时,安全功能需要持续维护和更新,及时修复发现的安全漏洞,应对新出现的安全威胁。
通过本文的学习,开发者可以掌握跨平台安全功能的开发方法,并应用到自己的项目中。安全功能不仅适用于笔记应用,还可以扩展到任何需要保护用户数据的场景,如聊天应用、文档管理、密码管理器等。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)