此前七篇文章,我们构建了一个功能完备的 Agent 框架:多渠道接入、自主推理、技能注入、多智能体协作、长短期记忆以及安全的工具沙箱。然而,所有这些能力若缺少一个坚实的安全底座,就无法真正应用于企业环境。企业 Agent 与个人助手最根本的区别在于信任模型:前者默认不信任任何人,后者默认信任机主。 今天,我们将系统性地加固整个框架,构建从网关到工具的纵深防御,实现多租户隔离、细粒度权限控制(RBAC)、全链路审计以及与 SSO/LDAP 的集成。
系列文章:
手把手构建企业级 Agent 框架:从 OpenClaw 架构到自主实现
手把手构建企业级 Agent 框架(二):Gateway 网关与多渠道接入
手把手构建企业级 Agent 框架(三):Pi Agent 运行时与 ReAct 循环
手把手构建企业级 Agent 框架(四):Skill 系统——知识注入与能力扩展
手把手构建企业级 Agent 框架(五):多智能体并行与任务委派,突破单 Agent 的性能与上下文瓶颈
手把手构建企业级 Agent 框架(六):双源记忆系统 — 让 Agent 真正记住你,跨越会话的智能回忆
手把手构建企业级 Agent 框架(七):工具系统与安全执行层 — 让 Agent 的双手安全而有力
一、企业 Agent 安全全景图
安全不是一层皮,而应贯穿每一层。下图展示了我们的“洋葱型”防御体系:

核心安全原则:
- 最小权限:每个工具、每个技能只能访问完成任务所必须的资源。
- 纵深防御:网关认证 → 运行时授权 → 工具执行前检查,任意一环失败即阻断。
- 不可否认性:所有操作均记录审计日志,可作为合规证据。
- 租户隔离:不同租户之间的数据、Agent 实例、记忆严格隔离。
二、认证与身份上下文传递
企业环境中,用户身份通常来自单点登录系统(如 Keycloak、Azure AD、LDAP)。我们的 Gateway 已经集成了 JWT 验证,现在需要强化为标准 OAuth2/OIDC 流程,并将用户角色和租户信息提取为 SecurityContext 贯穿整个请求链。
2.1 SecurityContext 定义
from dataclasses import dataclass, field
from typing import List, Dict, Optional
@dataclass
classSecurityContext:
user_id:str
tenant_id:str
roles: List[str]= field(default_factory=list)
permissions: List[str]= field(default_factory=list)# 细粒度权限
original_token: Optional[str]=None# 用于下游服务调用
2.2 Gateway 认证增强
在 Gateway 中,除了验证 JWT 签名,还需从 token 的 claims 中提取 roles、tenant_id,并构造 SecurityContext。同时支持 API Key 模式用于自动化服务。
# auth.py 增强版
import jwt
from fastapi import HTTPException, Request
from security_context import SecurityContext
ALGORITHM ="RS256"# 使用非对称密钥
PUBLIC_KEY ="..."
defauthenticate_request(request: Request)-> SecurityContext:
"""从请求中提取并验证身份,返回 SecurityContext"""
# 1. 尝试 Bearer Token
auth_header = request.headers.get("Authorization")
if auth_header and auth_header.startswith("Bearer "):
token = auth_header[7:]
payload = jwt.decode(token, PUBLIC_KEY, algorithms=[ALGORITHM], audience="eclaw-api")
return SecurityContext(
user_id=payload["sub"],
tenant_id=payload.get("tenant_id","default"),
roles=payload.get("realm_access",{}).get("roles",[]),
permissions=payload.get("permissions",[]),
original_token=token
)
# 2. 尝试 API Key
api_key = request.headers.get("X-API-Key")
if api_key:
# 从数据库或配置中查找 API Key 对应的服务账号
service_account = lookup_api_key(api_key)
if service_account:
return SecurityContext(**service_account)
raise HTTPException(status_code=401, detail="未提供有效的认证信息")
2.3 企业 SSO 集成模式
我们采用 Keycloak 作为身份提供者 (IdP),它支持 LDAP、SAML 等多种后端。Gateway 只负责验证 Keycloak 签发的 JWT,不直接接触用户密码,符合零信任架构。
🔐 快速搭建 Keycloak:
docker run -p8080:8080 -eKEYCLOAK_ADMIN=admin -eKEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:latest start-dev然后配置 realm、client 和用户角色,获取 JWKS 端点供 Gateway 验签。
三、细粒度权限控制 (RBAC + ABAC)
仅靠角色 (Role) 不足以应对复杂场景。我们实现一个 Policy Engine,支持基于角色和基于属性的混合授权。
3.1 权限模型设计
我们将资源操作定义为 资源:动作,例如 order:read、user:delete。每个工具注册时声明所需的权限,运行时检查 SecurityContext 是否拥有该权限。
# permissions.py
from enum import Enum
from typing import List, Set
classPermission:
ORDER_READ ="order:read"
ORDER_WRITE ="order:write"
USER_READ ="user:read"
FILE_READ ="file:read"
REFUND_EXECUTE ="refund:execute"
REPORT_GENERATE ="report:generate"
# 角色-权限映射(生产应从数据库或配置中心加载)
ROLE_PERMISSIONS ={
"user":[Permission.ORDER_READ, Permission.USER_READ],
"agent":[Permission.ORDER_READ, Permission.REPORT_GENERATE],
"admin":[Permission.ORDER_READ, Permission.ORDER_WRITE, Permission.USER_READ,
Permission.FILE_READ, Permission.REFUND_EXECUTE, Permission.REPORT_GENERATE],
}
3.2 PolicyEnforcer 实现
# policy_enforcer.py
from security_context import SecurityContext
from permissions import ROLE_PERMISSIONS, Permission
classPolicyEnforcer:
def__init__(self):
self.role_perms = ROLE_PERMISSIONS # 可从外部配置动态加载
defcheck_permission(self, context: SecurityContext, required_permission:str)->bool:
# 1. 直接检查 permissions 列表(可能是服务账户)
if required_permission in context.permissions:
returnTrue
# 2. 检查角色拥有的权限
for role in context.roles:
if required_permission in self.role_perms.get(role,[]):
returnTrue
returnFalse
defcheck_tool_access(self, context: SecurityContext, tool_required_roles: List[str], tool_required_perms: List[str])->bool:
# 角色检查
if tool_required_roles andnotset(tool_required_roles)&set(context.roles):
returnFalse
# 权限检查
for perm in tool_required_perms:
ifnot self.check_permission(context, perm):
returnFalse
returnTrue
3.3 将权限检查集成到 ToolExecutor
修改 tool_executor.py,在执行前调用 PolicyEnforcer:
classToolExecutor:
def__init__(self, registry: ToolRegistry, sandbox=None, policy: PolicyEnforcer =None):
self.policy = policy or PolicyEnforcer()
# ... 其他初始化
asyncdefexecute(self, name:str, params: Dict, context: SecurityContext)-> Any:
tool = self.registry.get(name)
ifnot tool:
raise ValueError(f"工具 '{name}' 未注册")
# 权限检查
ifnot self.policy.check_tool_access(context, tool.required_roles, tool.required_permissions):
raise PermissionError(f"用户 {context.user_id} 无权使用工具 {name}")
# ... 后续执行逻辑不变
同时更新 ToolDefinition,增加 required_permissions 字段。
四、全链路审计与合规
企业级系统必须回答:“谁在什么时间、做了什么操作、结果如何?” 我们的审计日志需要包括 Gateway 请求、Agent 推理步骤、工具调用和结果。建议日志输出到 ELK (Elasticsearch) 或直接对接 SIEM。
4.1 审计日志标准化
采用结构化 JSON 日志,包含统一字段:
# audit.py 增强
import json
import logging
from datetime import datetime, timezone
classAuditLogger:
def__init__(self, log_file="audit.json"):
self.logger = logging.getLogger("audit")
handler = logging.FileHandler(log_file)
self.logger.addHandler(handler)
self.logger.setLevel(logging.INFO)
deflog_event(self, event_type:str, context: SecurityContext, details:dict, status:str="success"):
entry ={
"timestamp": datetime.now(timezone.utc).isoformat(),
"event_type": event_type,# 如 'tool_call', 'agent_decision', 'gateway_request'
"user_id": context.user_id,
"tenant_id": context.tenant_id,
"roles": context.roles,
"status": status,
"details": details
}
self.logger.info(json.dumps(entry, ensure_ascii=False))
4.2 集成到各层
- Gateway: 记录每个请求的 URI、方法、用户和响应状态。
- Agent: 在每次
thought和tool_call时记录决策意图。 - ToolExecutor: 记录工具名、参数、执行结果和耗时。
五、数据保护与安全编码
⚠️ 防止 Prompt Injection
- 绝不将用户输入直接拼入系统提示词,始终通过参数化或模板渲染。
- 对用户输入进行清洗,检测“忽略之前指令”等攻击语句,可结合意图分类器。
- 限制 LLM 可访问的上下文范围,Skill 注入前也应做安全扫描。
对于敏感配置(如 API Key),使用环境变量或密钥管理服务(HashiCorp Vault)注入,避免硬编码。工具执行时的文件路径、URL 必须进行白名单验证,防止 SSRF 和目录遍历。
六、代码实战:安全模块集成与测试
我们将前面的安全组件组装到一起,改造之前的主程序入口。
6.1 项目结构
eclaw-security/
├── security_context.py
├── permissions.py
├── policy_enforcer.py
├── audit.py (增强版)
├── auth.py (增强版)
├── agent.py (集成 PolicyEnforcer)
├── tool_executor.py (集成权限检查)
├── main.py
└── test_security.py
6.2 启动入口 (main.py) 示例
from fastapi import FastAPI, Request, WebSocket
from auth import authenticate_request
from security_context import SecurityContext
from agent import AgentRuntime
from policy_enforcer import PolicyEnforcer
from audit import AuditLogger
app = FastAPI()
audit = AuditLogger()
policy = PolicyEnforcer()
agent = AgentRuntime(policy=policy, audit=audit)
@app.middleware("http")
asyncdefsecurity_middleware(request: Request, call_next):
# 排除健康检查等路径
if request.url.path in["/health"]:
returnawait call_next(request)
try:
ctx = authenticate_request(request)
request.state.security_context = ctx
except Exception as e:
audit.log_event("auth_failure", SecurityContext("unknown","unknown"),{"error":str(e)},"failed")
return JSONResponse(status_code=401, content={"detail":"Unauthorized"})
# 继续处理请求
response =await call_next(request)
audit.log_event("request", ctx,{"path": request.url.path,"method": request.method,"status": response.status_code})
return response
@app.websocket("/ws/chat")
asyncdefws_endpoint(ws: WebSocket):
# 认证在连接建立时完成
...
6.3 安全上下文在 Agent 中的传递
Agent 的 run() 方法接受一个 SecurityContext 参数,并传递给工具执行器:
asyncdefrun(self, session_id:str, user_input:str, ctx: SecurityContext):
# ... 在需要执行工具时
result =await self.executor.execute(tool_name, tool_params, ctx)
6.4 测试脚本
# test_security.py
import asyncio
from policy_enforcer import PolicyEnforcer
from security_context import SecurityContext
from permissions import Permission
asyncdeftest():
policy = PolicyEnforcer()
# 普通用户
user_ctx = SecurityContext(user_id="user001", tenant_id="t1", roles=["user"])
assert policy.check_permission(user_ctx, Permission.ORDER_READ)==True
assert policy.check_permission(user_ctx, Permission.REFUND_EXECUTE)==False
# 管理员
admin_ctx = SecurityContext(user_id="admin001", tenant_id="t1", roles=["admin"])
assert policy.check_permission(admin_ctx, Permission.REFUND_EXECUTE)==True
print("权限测试通过!")
if __name__ =="__main__":
asyncio.run(test())
七、与 OpenClaw 的对标思考
🔍 “我们做了什么” vs “OpenClaw 为什么这样做”?
认证机制: OpenClaw 个人版通常运行在本地,因而依赖操作系统用户权限,几乎不考虑多用户认证。我们的系统从 Gateway 开始就强制 JWT/OAuth2 认证,并能轻松对接企业 SSO,这是生产部署的前提。
权限控制: OpenClaw 默认信任用户,工具调用无权限检查。我们引入了 RBAC 与 ABAC,可精确控制到每个工具和资源操作,这在多租户 SaaS 环境不可或缺。
审计能力: OpenClaw 侧重于功能实现,日志多为调试用途。我们构建了结构化审计日志,支持合规性分析,并可接入 SIEM 进行异常检测。
信任边界: OpenClaw 的架构哲学是“个人拥有完全控制权”,我们的企业版翻转了这一模型,强调“最小权限”和“纵深防御”,使框架适用于金融、医疗等强监管行业。
总而言之,我们在 OpenClaw 的灵活架构基础上,包裹了一层企业级安全外壳,既不损失其优秀的 Agent 设计,又使其具备了商业部署的资质。
八、总结与下一步
本文我们:
- 构建了纵深防御的企业安全体系:Gateway 认证 → 运行时权限 → 工具沙箱。
- 实现了细粒度的 RBAC+ABAC 权限引擎,并与 ToolExecutor 深度集成。
- 引入了全链路审计日志,满足合规性要求。
- 讨论了防止 Prompt 注入、数据加密等安全编码实践。
下一篇文章预告:《第9篇:可观测性与生产化运维》—— 我们将为框架加上 OpenTelemetry 全链路追踪、健康检查和 Grafana 监控看板,并打包为 Docker Compose 一键部署,让它真正可运维。







