A2A 跨应用智能体通信:第三方技能互通权限管控实战

一、业务背景:跨应用智能体传统互通安全痛点
鸿蒙7 API26 基于AgentExtensionAbility提供原生端侧A2A(Agent-to-Agent)跨应用通信框架,解决政企多应用协同、第三方技能互通四大安全与开发难题:
- 无标准化鉴权,任意应用均可调用智能体
旧方案无统一校验入口,第三方恶意应用可随意连接本地Agent,读取隐私技能数据(管理员密码、病历、政务表单); - 缺少调用白名单管控
无法限定仅指定合作应用访问技能,全部应用无差别放行,不符合等保2.0最小访问权限规范; - 通信无会话隔离、无指令分级拦截
第三方应用可调用全部技能,无法区分「查询类低风险指令」「修改/删除高风险指令」; - 连接无生命周期管理,内存泄漏、后台保活冲突
多应用频繁连接/断开AgentExtensionAbility,未做代理销毁、监听解绑,商用7×24小时终端易出现服务卡死。
A2A核心架构角色定义
- Agent服务端(Server):提供技能的应用,注册
AgentExtensionAbility,对外暴露本地离线技能(四则运算、备忘录、设备管控、国密解密); - Agent客户端(Client):第三方调用方应用,通过
wantAgent.connectAgent建立跨进程通信通道; - 鉴权核心入口
onAuth:服务端唯一校验节点,获取调用方包名、握手凭证,实现白名单、分级权限拦截; - 双向数据通道
onData/sendData:客户端下发指令、服务端返回技能执行结果,支持长会话实时交互。
二、工程全量前置配置(服务端+客户端双端)
2.1 通用@kit依赖导入
import {
AgentExtensionAbility,
wantAgent,
common,
Want
} from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
const TAG = "A2A_Agent_Security";
const DOMAIN = 0x0017;
2.2 权限声明(两端必须配置互通权限)
module.json5 增加跨智能体交互系统权限:
"requestPermissions": [
{
"name": "ohos.permission.INTERACT_WITH_AGENT",
"reason": "$string:a2a_interact_skill",
"usedScene": { "abilities": ["EntryAbility"], "when": "always" }
}
]
2.3 服务端:注册Agent扩展能力(提供技能方)
"extensionAbilities": [
{
"name": "AgentExtService",
"srcEntry": "./ets/agentserver/AgentExtService.ets",
"type": "agent",
"exported": true, // 必须true,允许跨应用访问
"description": "本地离线技能服务,支持第三方A2A调用",
"metadata": [
{
"name": "agent_config",
"value": "$profile:agent_config.json"
}
]
}
]
2.4 agent_config.json 智能体基础配置(resources/base/profile)
{
"agentCards": [
{
"agentId": "local_enterprise_agent_001",
"name": "政企通用工具智能体",
"version": "1.0.0",
"capabilities": { "streaming": true },
"skills": [
{ "id": "skill_calc", "name": "四则运算查询", "risk": "low" },
{ "id": "skill_note_query", "name": "备忘录读取", "risk": "low" },
{ "id": "skill_brightness_set", "name": "屏幕亮度修改", "risk": "high" },
{ "id": "skill_admin_pwd_decrypt", "name": "管理员密码解密", "risk": "high" }
]
}
]
}
三、服务端完整实现:白名单鉴权+分级技能权限管控
文件路径:ets/agentserver/AgentExtService.ets
核心四层安全管控:
- 应用包名白名单拦截:仅合作企业应用允许连接;
- 握手Token二次校验:客户端携带预共享密钥握手;
- 技能风险分级拦截:低风险查询放行,高风险操作二次校验;
- 会话生命周期管理:连接、断开日志全埋点,防止内存泄漏。
export default class AgentExtService extends AgentExtensionAbility {
// 1. 可信第三方应用白名单(仅允许这些包名A2A互通)
private readonly ALLOW_BUNDLE_LIST = [
"com.enterprise.merchant",
"com.enterprise.gov.office"
];
// 2. 预共享握手密钥(政企多应用统一分发,生产存入HUKS加密存储)
private readonly SHARED_HANDSHAKE_TOKEN = "A2A_ENTERPRISE_2026_SECURE";
// 高风险技能黑名单,普通第三方禁止调用
private readonly HIGH_RISK_SKILL = ["skill_brightness_set", "skill_admin_pwd_decrypt"];
onCreate(): void {
hilog.info(DOMAIN, TAG, "A2A跨应用智能体服务启动就绪");
}
/**
* A2A核心鉴权入口:客户端发起连接时强制校验
* proxy:通信代理对象,可获取调用方信息、授权/拒绝连接
* handshakeData:客户端携带的握手凭证(预共享Token)
*/
onAuth(proxy: common.AgentHostProxy, handshakeData: string): void {
// 步骤1:获取发起调用的第三方应用包名
const callerBundle = proxy.getCallerBundleName();
hilog.info(DOMAIN, TAG, `收到A2A连接请求,调用方应用:${callerBundle}`);
// 校验1:应用包名不在白名单,直接拒绝连接
if (!this.ALLOW_BUNDLE_LIST.includes(callerBundle)) {
proxy.authorize("deny:bundle_not_allow");
hilog.warn(DOMAIN, TAG, `非法应用${callerBundle}被拦截,禁止A2A访问`);
return;
}
// 校验2:握手Token不匹配,拒绝连接
if (handshakeData !== this.SHARED_HANDSHAKE_TOKEN) {
proxy.authorize("deny:token_invalid");
hilog.warn(DOMAIN, TAG, `应用${callerBundle}握手密钥错误,连接拒绝`);
return;
}
// 双重校验通过,授予连接权限
proxy.authorize("success");
hilog.info(DOMAIN, TAG, `可信应用${callerBundle}鉴权成功,建立A2A通信会话`);
}
/**
* 接收第三方客户端下发指令,执行技能并分级权限拦截
*/
onData(proxy: common.AgentHostProxy, inputRaw: string): void {
const callerBundle = proxy.getCallerBundleName();
let req;
try {
req = JSON.parse(inputRaw);
} catch {
proxy.sendData(JSON.stringify({ code: -1, msg: "指令格式错误" }));
return;
}
const skillId = req.skillId;
const params = req.params;
hilog.info(DOMAIN, TAG, `A2A调用:应用${callerBundle} 请求技能${skillId}`);
// 分级权限拦截:高风险技能直接拒绝第三方调用
if (this.HIGH_RISK_SKILL.includes(skillId)) {
const res = JSON.stringify({
code: 403,
msg: "权限不足:第三方应用禁止调用高风险修改类技能"
});
proxy.sendData(res);
return;
}
// 分发低风险技能执行
const skillResult = this.dispatchSafeSkill(skillId, params);
proxy.sendData(JSON.stringify({ code: 0, data: skillResult }));
}
/** 低风险安全技能执行逻辑 */
private dispatchSafeSkill(skillId: string, params: Record<string, string>): string {
switch (skillId) {
case "skill_calc": {
const expr = params.expr.replace(/[^0-9\+\-\*\/\.]/g, "");
return `计算结果:${eval(expr)}`;
}
case "skill_note_query": {
return "备忘录列表:1.鸿蒙A2A跨应用智能体开发 2.商用Kiosk锁机配置";
}
default:
return "未识别的安全技能ID";
}
}
onDestroy(): void {
hilog.info(DOMAIN, TAG, "A2A智能体服务销毁,释放全部通信代理");
}
}
四、客户端完整实现:第三方应用发起A2A跨应用调用
第三方合作应用页面,携带握手Token建立跨应用连接,下发标准化技能指令,接收服务端返回结果。
import { ComponentV2, Local } from '@kit.ArkUI';
import { wantAgent, common, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = "A2A_Client_Demo";
// 服务端应用包名、Agent扩展Ability名称
const SERVER_BUNDLE = "com.enterprise.agent.server";
const SERVER_AGENT_ABILITY = "AgentExtService";
// 与服务端统一的握手凭证
const HANDSHAKE_TOKEN = "A2A_ENTERPRISE_2026_SECURE";
@Entry
@ComponentV2
struct A2AClientPage {
@Local inputSkillId: string = "skill_calc";
@Local inputParam: string = "123*45";
@Local receiveMsg: string = "未连接跨应用智能体";
private agentProxy: common.AgentHostProxy | null = null;
private targetWant: Want = {
bundleName: SERVER_BUNDLE,
abilityName: SERVER_AGENT_ABILITY
};
aboutToDisappear() {
// 页面销毁强制断开A2A会话,释放通信句柄
this.disconnectAgent();
}
/** 建立A2A跨应用连接,握手鉴权 */
async connectRemoteAgent() {
try {
this.agentProxy = await wantAgent.connectAgent(this.targetWant, {
// 握手数据,服务端onAuth接收校验
handshakeData: HANDSHAKE_TOKEN,
onConnect: (proxy) => {
this.receiveMsg = "A2A跨应用智能体连接成功,可下发技能指令";
// 监听服务端返回的技能执行结果
proxy.on("data", (msg: string) => {
this.receiveMsg = `服务端返回结果:\n${msg}`;
});
},
onDisconnect: () => {
this.agentProxy = null;
this.receiveMsg = "A2A跨应用会话已断开";
}
});
} catch (err) {
const e = err as BusinessError;
this.receiveMsg = `A2A连接失败:${e.code} ${e.message}`;
hilog.error(0x0017, TAG, `跨应用Agent连接异常:${JSON.stringify(err)}`);
}
}
/** 下发标准化技能指令给远端应用智能体 */
sendSkillCommand() {
if (!this.agentProxy || !this.inputSkillId.trim()) return;
// 标准化请求报文
const sendBody = JSON.stringify({
skillId: this.inputSkillId,
params: { expr: this.inputParam }
});
this.agentProxy.sendData(sendBody);
}
/** 断开A2A跨应用通信会话 */
disconnectAgent() {
if (this.agentProxy) {
wantAgent.disconnectAgent(this.agentProxy);
this.agentProxy = null;
}
}
build() {
Column({ space: 20 }) {
Text("A2A跨应用智能体|第三方客户端调用演示")
.fontSize(22)
.fontWeight(FontWeight.Bold)
.margin({ top: 20 });
Text(this.receiveMsg)
.width("90%")
.padding(16)
.backgroundColor("#F0F7FF")
.borderRadius(12)
.fontSize(14)
.whiteSpace(WhiteSpace.PreWrap);
Column({ space: 12 }) {
Row({ space: 10 }) {
Text("技能ID:").width(80);
TextInput({ text: this.inputSkillId })
.layoutWeight(1)
.onChange(v => this.inputSkillId = v);
}
Row({ space: 10 }) {
Text("参数:").width(80);
TextInput({ text: this.inputParam })
.layoutWeight(1)
.onChange(v => this.inputParam = v);
}
}
.width("90%");
Flex({ wrap: FlexWrap.Wrap, gap: 10 }) {
Button("1.连接远端第三方智能体").onClick(() => this.connectRemoteAgent());
Button("2.下发技能指令").backgroundColor("#0A59F7").onClick(() => this.sendSkillCommand());
Button("3.断开A2A会话").backgroundColor("#888").onClick(() => this.disconnectAgent());
}
.width("90%");
Text("可用安全技能:skill_calc、skill_note_query;高风险技能第三方禁止调用")
.fontSize(12)
.fontColor("#888")
.margin({ top: 10 });
Blank();
Text("💡 四层安全管控:白名单应用校验+握手Token+技能分级拦截+会话生命周期销毁")
.fontSize(12)
.fontColor("#AAAAAA")
.margin({ bottom: 30 });
}
.width("100%")
.height("100%")
.padding({ left: 16, right: 16 })
.backgroundColor("#F8F9FA");
}
}
五、进阶安全加固方案(政企商用必做)
5.1 握手Token存入HUKS硬件密钥托管,禁止硬编码
使用SecurityKit HUKS生成对称密钥,握手Token从TEE安全区读取,杜绝逆向抓取密钥:
// 伪代码:从HUKS读取预共享握手凭证
async function getA2AHandshakeToken(): Promise<string> {
const tokenRaw = await huks.getData("a2a_handshake_key");
return new TextDecoder().decode(tokenRaw);
}
5.2 高风险技能增加用户二次弹窗授权
修改密码、文件删除、硬件管控等高风险指令,即使在白名单内,也需要本地用户弹窗确认后再执行。
5.3 多应用动态白名单(MDM远程下发)
政企批量终端搭配HEM设备管理平台,远程更新ALLOW_BUNDLE_LIST白名单,无需重新打包应用。
5.4 A2A通信报文SM3哈希完整性校验
客户端发送指令携带SM3摘要,服务端校验报文未被篡改,防止中间人篡改指令越权操作:
// 客户端发送报文附加hash
const raw = JSON.stringify(body);
const hash = cryptoFramework.createMd("SM3").update(raw).digest();
const sendPack = { data: raw, sign: btoa(hash) };
5.5 会话超时自动断开
设置5分钟无通信自动断开agentProxy,防止长期闲置会话被恶意劫持。
六、高频商用开发踩坑完整排坑手册
坑1:connectAgent连接持续失败,返回权限拒绝
- 两端
module.json5未声明ohos.permission.INTERACT_WITH_AGENT互通权限; - 服务端AgentExtensionAbility未配置
exported:true,跨应用不可见; - bundleName、abilityName填写错误,与服务端工程配置不匹配;
- 使用调试自动签名,正式企业签名才允许跨应用Agent互通。
坑2:onAuth鉴权不触发,第三方直接连接成功
根因:客户端connectAgent未传入handshakeData参数,系统跳过握手校验;
修复:客户端必须携带握手Token,服务端才能进入onAuth鉴权流程。
坑3:白名单配置失效,陌生应用仍可连接
getCallerBundleName()获取包名大小写不匹配,对比判断失效;- 服务端未做全量字符串匹配,存在模糊匹配漏洞;
规范:包名全字符严格相等校验,统一小写对比。
坑4:第三方可调用高风险隐私技能,权限拦截失效
根因:技能分级判断写在onData分支末尾,未提前拦截;
规范:进入onData第一时间判断技能风险等级,高风险直接返回403拒绝,不执行业务逻辑。
坑5:页面反复连接Agent,句柄泄漏、服务卡死
解决方案:页面aboutToDisappear强制调用wantAgent.disconnectAgent销毁代理,商用7×24小时终端必须做资源回收。
坑6:模拟器无法调试A2A跨应用通信
模拟器无跨进程Agent路由服务,必须两台真机分别安装客户端、服务端应用,同账号组网测试。
坑7:传输敏感明文数据存在泄露风险
优化:A2A传输前使用HUKS SM4加密报文,跨进程仅传输密文,服务端本地TEE解密后执行技能。
七、政企商用生产级最佳实践
- 四层安全标准化流程
应用白名单校验 → 握手密钥Token校验 → 技能风险分级拦截 → 报文哈希完整性校验,缺一不可; - 最小权限原则
第三方仅开放查询类低风险技能,修改、解密、硬件管控等高风险技能仅允许自身应用调用; - 全链路操作审计日志
A2A连接建立/断开、鉴权成功/拒绝、技能调用、指令参数全部打印hilog,安全事件可追溯; - 离线无网络兼容
A2A端侧通信完全本地进程交互,不依赖云端,适配政务、门店断网自助终端; - 多智能体协同兼容
A2A可搭配DistributedMultiKit多端同步、Kiosk锁机、本地AgentExtensionAbility技能体系,形成完整政企端侧AI协同方案; - 签名强绑定隔离
Agent服务与应用签名证书绑定,其他同名包签名不一致无法通过鉴权,防止仿冒应用劫持A2A通道。
八、方案总结
鸿蒙7端侧A2A跨应用智能体通信基于AgentExtensionAbility实现标准化第三方技能互通,依托onAuth统一鉴权入口构建白名单应用管控、握手密钥校验、技能分级权限拦截三层核心安全体系,彻底解决第三方应用随意访问本地隐私技能、越权操作等政企合规痛点。
整套方案纯本地端侧运行、无网络依赖,支持双向长会话实时交互,适配政务多应用协同、零售门店多终端技能互通、企业内部工具联动等商用场景,是鸿蒙跨应用智能体互通的安全标准化落地方案。
更多推荐



所有评论(0)