# 第十七章:授权架构模式 > "好的授权架构应该像空气一样:无处不在,但用户感觉不到它的存在。" ```{mermaid} mindmap root((授权架构)) 演进 硬编码 配置文件 策略引擎 外部化授权 XACML 架构 PEP PDP PIP PAP 微服务模式 Gateway Service Mesh 应用层 混合 多租户 租户隔离 权限继承 跨租户 ``` ## 17.1 授权架构的演进 ``` Level 0: 硬编码 if user.role == "admin": allow() → 问题:修改权限需要改代码、重新部署 Level 1: 配置文件 permissions = load_config("permissions.yaml") → 问题:缺乏测试、审计,配置散落各处 Level 2: 策略引擎 result = opa.evaluate(input) → 改进:策略与代码分离,可测试 Level 3: 外部化授权(Authorization as a Service) result = authz_service.check(user, action, resource) → 最佳:统一授权服务,所有应用共享 ``` ## 17.2 XACML 授权架构 ``` ┌──────────┐ ┌──────────┐ ┌──────────┐ │ 用户 │───▶│ PEP │───▶│ 应用 │ │ │ │ 策略执行点 │ │ 资源 │ └──────────┘ └────┬─────┘ └──────────┘ │ │ 授权请求 ▼ ┌──────────┐ │ PDP │◀──── ┌──────────┐ │ 策略决策点 │ │ PAP │ │ (OPA/ │ │ 策略管理点 │ │ OpenFGA)│ │ (管理界面) │ └────┬─────┘ └──────────┘ │ │ 查询属性 ▼ ┌──────────┐ │ PIP │ │ 策略信息点 │ │ (用户属性 │ │ 资源属性) │ └──────────┘ ``` | 组件 | 职责 | 实现 | |------|------|------| | PEP | 拦截请求,执行决策 | API Gateway、中间件、Sidecar | | PDP | 评估策略,做出决策 | OPA、OpenFGA、Cedar | | PIP | 提供决策所需的属性数据 | 用户服务、LDAP、数据库 | | PAP | 管理策略的创建和更新 | 管理界面、Git | ## 17.3 微服务授权模式 ### 模式 1:Gateway 集中授权 ``` ┌──────────┐ ┌──────────────┐ ┌──────────┐ │ 客户端 │───▶│ API Gateway │───▶│ 微服务 │ │ │ │ ┌─────────┐ │ │ 信任 GW │ │ │ │ │ 认证 │ │ │ 不做授权 │ │ │ │ │ 授权 ✅ │ │ │ │ │ │ │ │ 限流 │ │ │ │ │ │ │ └─────────┘ │ │ │ └──────────┘ └──────────────┘ └──────────┘ ``` **优点**:简单、集中管理 **缺点**:粗粒度、Gateway 成为瓶颈 ### 模式 2:Service Mesh 授权 ``` ┌──────────────────────────────────────┐ │ Kubernetes Pod │ │ ┌──────────┐ ┌────────────────┐ │ │ │ 微服务 │◀──▶│ Envoy Sidecar │ │ │ │ 业务逻辑 │ │ ┌──────────┐ │ │ │ │ │ │ │ mTLS │ │ │ │ │ │ │ │ AuthzPolicy│ │ │ │ │ │ │ └──────────┘ │ │ │ └──────────┘ └────────────────┘ │ └──────────────────────────────────────┘ ``` ### 模式 3:应用层授权 ```python # 应用内授权 — 最细粒度 @app.put("/api/documents/{doc_id}") async def update_document( doc_id: str, user: User = Depends(get_current_user), ): # 调用外部授权服务 allowed = await openfga_client.check( user=f"user:{user.id}", relation="can_edit", object=f"document:{doc_id}", ) if not allowed: raise HTTPException(403, "Cannot edit this document") # 业务逻辑... ``` ### 模式 4:混合模式(推荐) ``` ┌──────────┐ ┌──────────────┐ ┌──────────────────┐ │ 客户端 │───▶│ API Gateway │───▶│ 微服务 │ │ │ │ 粗粒度授权 │ │ ┌──────────────┐ │ │ │ │ • 认证 │ │ │ 细粒度授权 │ │ │ │ │ • 角色检查 │ │ │ • 资源级权限 │ │ │ │ │ • 速率限制 │ │ │ • OpenFGA │ │ │ │ │ │ │ │ • OPA │ │ │ │ │ │ │ └──────────────┘ │ └──────────┘ └──────────────┘ └──────────────────┘ ``` ## 17.4 多租户授权设计 ``` 多租户授权模型(OpenFGA): type user type tenant relations define admin: [user] define member: [user] or admin type project relations define tenant: [tenant] define admin: [user] or admin from tenant define member: [user] or admin define can_manage: admin define can_access: member or can_manage type resource relations define project: [project] define owner: [user] define editor: [user] or owner or can_manage from project define viewer: [user] or editor or can_access from project ``` ### 租户隔离策略 | 策略 | 描述 | 实现 | |------|------|------| | 数据隔离 | 每个租户的数据完全隔离 | 数据库 schema/表前缀 | | 权限隔离 | 租户 A 的用户无法访问租户 B | OpenFGA 关系检查 | | 管理隔离 | 租户管理员只能管理自己的租户 | 租户级 admin 角色 | | 跨租户访问 | 特殊场景下的跨租户协作 | 显式的跨租户关系元组 | ## 17.5 性能优化 ### 缓存策略 ```python import redis import json from functools import lru_cache r = redis.Redis() async def check_permission_cached(user_id: str, relation: str, object_id: str) -> bool: """带缓存的权限检查""" cache_key = f"authz:{user_id}:{relation}:{object_id}" # 1. 检查缓存 cached = r.get(cache_key) if cached is not None: return cached == b"1" # 2. 调用 OpenFGA result = await openfga_client.check( user=f"user:{user_id}", relation=relation, object=object_id, ) # 3. 写入缓存(TTL 60秒) r.setex(cache_key, 60, "1" if result.allowed else "0") return result.allowed def invalidate_permission_cache(object_id: str): """权限变更时清除相关缓存""" pattern = f"authz:*:*:{object_id}" for key in r.scan_iter(pattern): r.delete(key) ``` ### 批量检查 ```python async def batch_check_permissions(user_id: str, objects: list[str], relation: str) -> dict: """批量权限检查""" results = {} checks = [ openfga_client.check( user=f"user:{user_id}", relation=relation, object=obj, ) for obj in objects ] responses = await asyncio.gather(*checks) for obj, resp in zip(objects, responses): results[obj] = resp.allowed return results ``` ## 17.6 实战:SaaS 平台授权架构 ``` ┌─────────────────────────────────────────────────────┐ │ SaaS 平台授权架构 │ │ │ │ ┌──────────┐ ┌──────────────┐ ┌───────────┐ │ │ │ Web/Mobile│───▶│ API Gateway │───▶│ 微服务集群 │ │ │ │ 客户端 │ │ (Kong/Envoy) │ │ │ │ │ └──────────┘ │ • JWT 验证 │ │ ┌───────┐ │ │ │ │ • 租户路由 │ │ │用户服务│ │ │ │ │ • 速率限制 │ │ └───┬───┘ │ │ │ └──────────────┘ │ │ │ │ │ │ ▼ │ │ │ ┌──────────────────────────────┐ │ ┌───────┐ │ │ │ │ 授权服务层 │ │ │项目服务│ │ │ │ │ ┌──────────┐ ┌───────────┐ │ │ └───┬───┘ │ │ │ │ │ OpenFGA │ │ OPA │ │ │ │ │ │ │ │ │ (ReBAC) │ │ (ABAC) │ │ │ ▼ │ │ │ │ │ 关系权限 │ │ 条件策略 │ │ │ ┌───────┐ │ │ │ │ └──────────┘ └───────────┘ │ │ │资源服务│ │ │ │ └──────────────────────────────┘ │ └───────┘ │ │ │ └───────────┘ │ └─────────────────────────────────────────────────────┘ ``` ## 17.7 小结 - 授权架构从**硬编码**演进到**外部化授权即服务** - **XACML 架构**(PEP/PDP/PIP/PAP)是授权系统的标准参考架构 - **混合模式**最实用:Gateway 粗粒度 + 应用层细粒度 - **多租户授权**需要数据隔离、权限隔离、管理隔离 - **缓存和批量检查**是授权性能优化的关键手段 - OpenFGA(ReBAC)+ OPA(ABAC)的组合覆盖大多数授权需求