应用账户HarmonyOS开发小实践
一、小知识
你打开手机上的「运动健康」App,它提示你「使用华为账号登录」——点击后,跳转到一个授权页面,确认后你就自动登录了。你不需要重新输入用户名密码,甚至不需要知道密码是什么。这背后的功臣,就是应用账户。
应用账户和系统账户完全是两个维度的东西。系统账户管的是"谁在用这台设备",而应用账户管的是"谁在用这个 App"。一个系统账户下可以有多个应用账户,就像一个人可以有微信账号、淘宝账号、抖音账号一样。
但应用账户的价值远不止"记住用户名密码"。它最强大的能力是跨应用账户授权——让 App A 安全地访问 App B 的账户信息,而不需要用户重复登录。这就是 HarmonyOS 生态内"一次登录,全生态通行"的技术基础。
二、核心原理
2.1 应用账户体系架构

flowchart TB
subgraph 应用A["应用A(资源拥有者)"]
A1[AppAccountManager]
A2[账户数据存储]
A3[OAuth令牌]
end
subgraph 应用B["应用B(资源请求者)"]
B1[AppAccountManager]
B2[授权请求]
end
subgraph 系统["系统服务"]
S1[AccountManager Service]
S2[授权管理器]
S3[令牌管理器]
end
B1 -->|1.请求授权| S2
S2 -->|2.弹出授权确认| B1
B1 -->|3.用户确认| S2
S2 -->|4.发放授权| S1
S1 -->|5.返回令牌| B1
B1 -->|6.使用令牌访问| A1
A1 -->|7.验证令牌并返回数据| B1
classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef error fill:#F44336,stroke:#D32F2F,color:#fff
classDef info fill:#2196F3,stroke:#1976D2,color:#fff
classDef purple fill:#9C27B0,stroke:#7B1FA2,color:#fff
class A1,A2,A3 primary
class B1,B2 info
class S1,S2,S3 purple
2.2 核心概念
| 概念 | 说明 | 类比 |
|---|---|---|
| AppAccountManager | 应用账户管理器,所有操作的入口 | 银行柜员 |
| 账户名(name) | 应用内唯一标识用户的名称 | 银行卡号 |
| 所有者(owner) | 创建该账户的应用包名 | 开户行 |
| 授权(Authorization) | 允许其他应用访问账户信息 | 授权书 |
| OAuth令牌(Token) | 授权后颁发的访问凭证 | 临时通行证 |
| 自定义数据 | 与账户关联的键值对数据 | 账户备注信息 |
2.3 跨应用授权流程
跨应用授权是应用账户最核心的能力。整个流程就像你去银行办理业务——你(应用B)拿着身份证(授权请求)到柜台(系统服务),柜员确认你的身份后,给你一张临时通行证(OAuth令牌),你拿着通行证就能访问金库(应用A的数据)。
sequenceDiagram
participant AppB as 应用B
participant System as 系统服务
participant User as 用户
participant AppA as 应用A
AppB->>System: 1. createAccountCredential(name, credentialType, credential)
System->>System: 2. 查找目标账户(owner+name)
System->>User: 3. 弹出授权确认对话框
User->>System: 4. 确认授权
System->>System: 5. 生成授权令牌
System->>AppB: 6. 返回授权结果
AppB->>AppA: 7. 使用令牌请求资源
AppA->>System: 8. 验证令牌有效性
System->>AppA: 9. 令牌有效
AppA->>AppB: 10. 返回请求的资源
三、代码实战
3.1 应用账户的创建与管理
最基础的操作:在自己的应用中创建、查询和管理应用账户。
import { appAccount } from '@kit.BasicServicesKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 应用账户管理器
* 封装应用账户的创建、查询、更新和删除操作
*/
class AppAccountManager {
private manager: appAccount.AppAccountManager;
constructor() {
this.manager = appAccount.createAppAccountManager();
}
/**
* 创建一个新的应用账户
* @param name 账户名称(应用内唯一)
*/
async createAccount(name: string): Promise<boolean> {
try {
await this.manager.createAccount(name);
console.info(`[AppAccount] 账户创建成功: ${name}`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[AppAccount] 创建账户失败: code=${err.code}, msg=${err.message}`);
// 常见错误码处理
if (err.code === 12300001) {
console.error('[AppAccount] 账户名称已存在');
} else if (err.code === 12300002) {
console.error('[AppAccount] 账户名称格式不合法');
}
return false;
}
}
/**
* 创建账户并设置额外信息
* @param name 账户名称
* @param extraInfo 额外信息(JSON字符串)
*/
async createAccountWithExtraInfo(name: string, extraInfo: string): Promise<boolean> {
try {
await this.manager.createAccount(name, { extraInfo: extraInfo });
console.info(`[AppAccount] 账户创建成功(含额外信息): ${name}`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[AppAccount] 创建账户失败: ${err.message}`);
return false;
}
}
/**
* 查询当前应用的所有账户
*/
async queryAllAccounts(): Promise<appAccount.AppAccountInfo[]> {
try {
// 获取当前应用的包名作为owner
const accounts = await this.manager.queryAllAccounts();
console.info(`[AppAccount] 当前应用共有 ${accounts.length} 个账户`);
accounts.forEach((account, index) => {
console.info(` 账户${index + 1}: name=${account.name}, owner=${account.owner}`);
});
return accounts;
} catch (error) {
const err = error as BusinessError;
console.error(`[AppAccount] 查询账户失败: ${err.message}`);
return [];
}
}
/**
* 查询指定所有者应用的账户列表
* 用于跨应用场景下查询目标应用的账户
* @param owner 目标应用的包名
*/
async queryAccountsByOwner(owner: string): Promise<appAccount.AppAccountInfo[]> {
try {
const accounts = await this.manager.getAccountsByOwner(owner);
console.info(`[AppAccount] 应用${owner}有 ${accounts.length} 个账户`);
return accounts;
} catch (error) {
const err = error as BusinessError;
console.error(`[AppAccount] 查询指定owner账户失败: ${err.message}`);
return [];
}
}
/**
* 删除指定账户
* @param name 要删除的账户名称
*/
async removeAccount(name: string): Promise<boolean> {
try {
await this.manager.removeAccount(name);
console.info(`[AppAccount] 账户已删除: ${name}`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[AppAccount] 删除账户失败: ${err.message}`);
return false;
}
}
/**
* 设置账户的自定义数据
* 自定义数据以键值对形式存储,可用于同步用户偏好等
* @param name 账户名称
* @param key 数据键
* @param value 数据值
*/
async setCustomData(name: string, key: string, value: string): Promise<boolean> {
try {
await this.manager.setCustomData(name, key, value);
console.info(`[AppAccount] 自定义数据已设置: ${key}=${value}`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[AppAccount] 设置自定义数据失败: ${err.message}`);
return false;
}
}
/**
* 获取账户的自定义数据
* @param name 账户名称
* @param key 数据键
*/
async getCustomData(name: string, key: string): Promise<string> {
try {
const value = await this.manager.getCustomData(name, key);
console.info(`[AppAccount] 自定义数据: ${key}=${value}`);
return value;
} catch (error) {
const err = error as BusinessError;
console.error(`[AppAccount] 获取自定义数据失败: ${err.message}`);
return '';
}
}
}
// 使用示例:社交应用的用户管理
async function demoSocialApp() {
const accountManager = new AppAccountManager();
// 创建用户账户
await accountManager.createAccountWithExtraInfo(
'user_zhangsan',
JSON.stringify({ nickname: '张三', avatar: 'avatar_001.png', level: 5 })
);
// 设置自定义数据
await accountManager.setCustomData('user_zhangsan', 'theme', 'dark');
await accountManager.setCustomData('user_zhangsan', 'language', 'zh-CN');
// 读取自定义数据
const theme = await accountManager.getCustomData('user_zhangsan', 'theme');
console.info(`[Demo] 用户主题偏好: ${theme}`);
// 查询所有账户
await accountManager.queryAllAccounts();
}
3.2 跨应用账户授权
这是应用账户最核心的能力——让一个应用安全地访问另一个应用的账户信息。
import { appAccount } from '@kit.BasicServicesKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 跨应用授权管理器
* 处理应用间的账户授权与令牌管理
*/
class CrossAppAuthManager {
private manager: appAccount.AppAccountManager;
constructor() {
this.manager = appAccount.createAppAccountManager();
}
/**
* 【授权方】设置账户的授权列表
* 指定哪些应用可以访问本应用的账户
* @param name 账户名称
* @param authType 授权类型(如"OAuth2")
* @param authorizedAppList 被授权的应用包名列表
*/
async setAuthorizedApps(name: string, authType: string, authorizedAppList: string[]): Promise<boolean> {
try {
for (const app of authorizedAppList) {
await this.manager.setAppAccess(name, app, true);
console.info(`[CrossAuth] 已授权应用 ${app} 访问账户 ${name}`);
}
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[CrossAuth] 设置授权失败: ${err.message}`);
return false;
}
}
/**
* 【授权方】创建OAuth令牌
* 为已授权的应用颁发访问令牌
* @param name 账户名称
* @param authType 授权类型
* @param token 令牌内容
*/
async createOAuthToken(name: string, authType: string, token: string): Promise<boolean> {
try {
await this.manager.createAccountCredential(name, authType, token);
console.info(`[CrossAuth] OAuth令牌创建成功: account=${name}, type=${authType}`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[CrossAuth] 创建令牌失败: ${err.message}`);
return false;
}
}
/**
* 【请求方】请求获取目标应用的账户授权
* @param name 目标账户名称
* @param owner 目标应用包名
* @param authType 授权类型
*/
async requestAuthorization(name: string, owner: string, authType: string): Promise<string> {
try {
// 发起授权请求,系统会弹出授权确认对话框
const authResult = await this.manager.getAuthorizerInterface(name, owner, authType);
console.info(`[CrossAuth] 授权请求已发送: account=${name}, owner=${owner}`);
// 获取授权令牌
const token = await this.manager.getAccountCredential(name, owner, authType);
console.info(`[CrossAuth] 获取到授权令牌`);
return token;
} catch (error) {
const err = error as BusinessError;
console.error(`[CrossAuth] 请求授权失败: code=${err.code}, msg=${err.message}`);
if (err.code === 12300001) {
console.error('[CrossAuth] 目标账户不存在');
} else if (err.code === 12300004) {
console.error('[CrossAuth] 用户拒绝授权');
}
return '';
}
}
/**
* 【请求方】检查是否已获得授权
* @param name 目标账户名称
* @param owner 目标应用包名
*/
async checkAuthorization(name: string, owner: string): Promise<boolean> {
try {
// 尝试获取账户信息来验证授权状态
const accountInfo = await this.manager.getAccountInfo(owner, name);
return accountInfo !== null;
} catch {
return false;
}
}
/**
* 【授权方】撤销对指定应用的授权
* @param name 账户名称
* @param app 要撤销授权的应用包名
*/
async revokeAuthorization(name: string, app: string): Promise<boolean> {
try {
await this.manager.setAppAccess(name, app, false);
console.info(`[CrossAuth] 已撤销应用 ${app} 的授权`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[CrossAuth] 撤销授权失败: ${err.message}`);
return false;
}
}
}
// 使用示例:华为账号登录第三方应用
async function demoHuaweiAccountLogin() {
const authManager = new CrossAppAuthManager();
// ===== 华为账号App(授权方)的操作 =====
// 设置授权:允许"运动健康"和"应用市场"访问华为账号
await authManager.setAuthorizedApps(
'huawei_user_001',
'OAuth2',
['com.example.health', 'com.example.appmarket']
);
// 为已授权的应用创建OAuth令牌
await authManager.createOAuthToken(
'huawei_user_001',
'OAuth2',
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...'
);
// ===== 运动健康App(请求方)的操作 =====
// 请求华为账号的授权
const token = await authManager.requestAuthorization(
'huawei_user_001',
'com.huawei.hwid',
'OAuth2'
);
if (token) {
console.info('[Demo] 登录成功,获取到访问令牌');
}
}
3.3 账户数据同步
应用账户支持自定义数据的存储和跨设备同步,这对于多端协同场景至关重要。
import { appAccount } from '@kit.BasicServicesKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 账户数据同步管理器
* 管理应用账户的配置数据和同步状态
*/
class AccountSyncManager {
private manager: appAccount.AppAccountManager;
constructor() {
this.manager = appAccount.createAppAccountManager();
}
/**
* 保存用户配置到账户数据
* 这些数据会随账户在设备间同步
* @param accountName 账户名称
* @param config 用户配置对象
*/
async saveUserConfig(accountName: string, config: Record<string, string>): Promise<boolean> {
try {
// 逐个设置自定义数据
const entries = Object.entries(config);
for (const [key, value] of entries) {
await this.manager.setCustomData(accountName, key, value);
}
console.info(`[AccountSync] 用户配置已保存,共 ${entries.length} 项`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[AccountSync] 保存配置失败: ${err.message}`);
return false;
}
}
/**
* 加载用户配置
* @param accountName 账户名称
* @param keys 需要加载的配置键列表
*/
async loadUserConfig(accountName: string, keys: string[]): Promise<Record<string, string>> {
const config: Record<string, string> = {};
try {
for (const key of keys) {
try {
const value = await this.manager.getCustomData(accountName, key);
if (value) {
config[key] = value;
}
} catch {
// 某个键不存在不影响其他键的加载
console.warn(`[AccountSync] 配置键"${key}"不存在`);
}
}
console.info(`[AccountSync] 已加载 ${Object.keys(config).length} 项配置`);
return config;
} catch (error) {
const err = error as BusinessError;
console.error(`[AccountSync] 加载配置失败: ${err.message}`);
return {};
}
}
/**
* 同步账户数据到关联设备
* 通过设置关联数据来触发同步机制
* @param accountName 账户名称
* @param syncData 需要同步的数据
*/
async syncAccountData(accountName: string, syncData: appAccount.CustomData): Promise<boolean> {
try {
// 设置关联数据,系统会自动处理跨设备同步
await this.manager.setCustomData(accountName, 'sync_timestamp', Date.now().toString());
await this.manager.setCustomData(accountName, 'sync_data', JSON.stringify(syncData));
console.info(`[AccountSync] 数据同步已触发: account=${accountName}`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[AccountSync] 数据同步失败: ${err.message}`);
return false;
}
}
/**
* 监听账户数据变化
* 当其他设备修改了账户数据,本地会收到通知
* @param owner 账户所有者包名
* @param name 账户名称
* @param callback 数据变化回调
*/
onAccountDataChange(
owner: string,
name: string,
callback: (accountInfo: appAccount.AppAccountInfo) => void
): void {
try {
this.manager.on('accountChange', [name], (accounts: appAccount.AppAccountInfo[]) => {
console.info(`[AccountSync] 收到账户数据变化通知,共 ${accounts.length} 个账户`);
accounts.forEach(account => {
if (account.name === name) {
callback(account);
}
});
});
console.info(`[AccountSync] 已注册数据变化监听: ${name}`);
} catch (error) {
const err = error as BusinessError;
console.error(`[AccountSync] 注册监听失败: ${err.message}`);
}
}
/**
* 取消数据变化监听
*/
offAccountDataChange(): void {
try {
this.manager.off('accountChange');
console.info('[AccountSync] 已取消数据变化监听');
} catch (error) {
const err = error as BusinessError;
console.error(`[AccountSync] 取消监听失败: ${err.message}`);
}
}
}
// 使用示例:笔记应用的多端同步
async function demoNoteAppSync() {
const syncManager = new AccountSyncManager();
// 在手机端保存用户配置
await syncManager.saveUserConfig('note_user_001', {
'theme': 'dark',
'font_size': '16',
'sort_order': 'modified_time',
'auto_sync': 'true',
});
// 在平板端加载用户配置
const config = await syncManager.loadUserConfig('note_user_001', [
'theme', 'font_size', 'sort_order', 'auto_sync'
]);
console.info(`[Demo] 加载的配置: ${JSON.stringify(config)}`);
// 监听数据变化
syncManager.onAccountDataChange('com.example.notes', 'note_user_001', (accountInfo) => {
console.info(`[Demo] 账户数据已更新: ${accountInfo.name}`);
// 重新加载配置
});
// 触发数据同步
await syncManager.syncAccountData('note_user_001', {
key: 'last_note_id',
value: 'note_20240101_001',
});
}
四、踩坑与注意事项
4.1 账户名命名规范
账户名有严格的格式要求,不符合规范的名称会被直接拒绝:
// ✅ 正确的命名
const validNames = ['user_001', 'zhangsan', 'test_account'];
// ❌ 错误的命名(会导致创建失败)
const invalidNames = [
'用户001', // 不支持中文
'user-001', // 不支持连字符
'user.001', // 不支持点号
'user 001', // 不支持空格
'', // 空字符串
];
4.2 跨应用授权的权限声明
跨应用授权需要在 module.json5 中声明相关权限:
{
"requestPermissions": [
{
"name": "ohos.permission.GET_APP_ACCOUNTS",
"reason": "$string:app_account_reason"
}
]
}
踩坑:即使声明了权限,跨应用访问也需要目标应用主动调用 setAppAccess 开放访问权限。这不是声明了就能用的,必须双方配合。
4.3 令牌安全存储
OAuth 令牌属于敏感数据,不要直接存储在自定义数据中:
// ❌ 危险:将令牌存储在自定义数据中(可能被其他应用读取)
await manager.setCustomData('user_001', 'access_token', token);
// ✅ 安全:使用专门的凭证管理接口
await manager.createAccountCredential('user_001', 'OAuth2', token);
// ✅ 更安全:令牌存储在 HUKS 加密区域
// 参见第205篇安全区域相关内容
4.4 账户删除的级联效应
删除应用账户时,相关的授权关系、令牌和自定义数据都会被级联删除。如果其他应用正在依赖这些数据,可能导致功能异常:
async function safeRemoveAccount(name: string): Promise<boolean> {
const manager = appAccount.createAppAccountManager();
// 1. 先检查是否有其他应用正在使用该账户
// 2. 通知依赖应用即将删除
// 3. 清理关联的OAuth令牌
// 4. 最后删除账户
try {
await manager.removeAccount(name);
console.info('[SafeRemove] 账户及关联数据已清理');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[SafeRemove] 删除失败: ${err.message}`);
return false;
}
}
4.5 异步操作的并发控制
多个异步操作同时修改同一个账户的数据可能导致数据竞争:
// ❌ 危险:并发修改
async function unsafeConcurrentUpdate(name: string) {
// 两个操作可能同时执行,导致数据不一致
manager.setCustomData(name, 'key1', 'value1');
manager.setCustomData(name, 'key2', 'value2');
}
// ✅ 安全:串行执行
async function safeSequentialUpdate(name: string) {
await manager.setCustomData(name, 'key1', 'value1');
await manager.setCustomData(name, 'key2', 'value2');
}
五、HarmonyOS 6 适配
5.1 API 变更
| 变更项 | HarmonyOS 5 | HarmonyOS 6 |
|---|---|---|
| 账户创建 | createAccount(name) |
新增 createAccountFull(name, options) |
| 授权管理 | setAppAccess |
新增 setAppAccessWithScope 支持细粒度权限范围 |
| 令牌管理 | createAccountCredential |
新增 createCredentialWithExpiry 支持令牌过期时间 |
| 数据同步 | setCustomData |
新增 syncCustomData 原生同步API |
| 事件监听 | on('accountChange') |
新增 on('credentialChange') 令牌变化通知 |
5.2 迁移指南
// HarmonyOS 5 写法
await manager.createAccount('user_001');
// HarmonyOS 6 推荐写法
const options: appAccount.CreateAccountOptions = {
customData: {
'nickname': '张三',
'avatar': 'avatar_001.png',
},
// HarmonyOS 6 新增:预设授权列表
authorizedApps: ['com.example.health'],
};
await manager.createAccountFull('user_001', options);
5.3 细粒度授权范围
HarmonyOS 6 支持更精细的授权范围控制,不再只是"全部授权"或"不授权":
// HarmonyOS 6: 按范围授权
const scopes = ['profile:read', 'friends:read', 'health:write'];
await manager.setAppAccessWithScope('user_001', 'com.example.health', true, scopes);
六、总结
核心知识点回顾
应用账户(appAccount)
├── 基础操作
│ ├── 创建 ── createAccount / createAccountFull
│ ├── 查询 ── queryAllAccounts / getAccountsByOwner
│ ├── 删除 ── removeAccount(级联删除关联数据)
│ └── 自定义数据 ── setCustomData / getCustomData
├── 跨应用授权
│ ├── 授权方 ── setAppAccess / setAppAccessWithScope
│ ├── 请求方 ── getAuthorizerInterface / getAccountCredential
│ ├── 令牌管理 ── createAccountCredential
│ └── 撤销授权 ── setAppAccess(name, app, false)
├── 数据同步
│ ├── 自定义数据 ── 键值对存储,支持跨设备同步
│ ├── 变化监听 ── on('accountChange')
│ └── 同步触发 ── syncCustomData
└── 注意事项
├── 命名规范 ── 仅支持字母、数字、下划线
├── 权限声明 ── GET_APP_ACCOUNTS
├── 令牌安全 ── 使用凭证接口而非自定义数据
├── 级联删除 ── 删除账户会清除所有关联数据
└── 并发控制 ── 避免同时修改同一账户数据
应用账户是 HarmonyOS 生态内"一次登录,全生态通行"的技术基石。理解了跨应用授权、OAuth 令牌管理和数据同步机制,你就能构建出流畅的多应用协同体验。记住:应用账户不是简单的用户名密码存储,而是一套完整的跨应用身份互通体系。
- 点赞
- 收藏
- 关注作者
评论(0)