3. 权限验证基础:认证与授权的区别及常见权限模型

在构建微服务与 BFF(Backend For Frontend)架构时,权限验证是保障系统安全的第一道防线。很多初学者容易混淆“认证”与“授权”这两个核心概念,导致在架构设计阶段就埋下安全隐患。本章将从基础概念出发,厘清认证与授权的本质区别,并深入剖析三种主流权限模型:RBAC、ABAC 与 PBAC,为后续 GoBFF 实战中的边界验证打下坚实的理论基础。

3.1 认证(Authentication)与授权(Authorization)的区别

认证与授权虽然经常被一起提及,但它们在安全体系中承担着完全不同的职责。简单来说:认证解决“你是谁”的问题,授权解决“你能做什么”的问题。

维度 认证(Authentication) 授权(Authorization)
核心问题 你是谁? 你能做什么?
执行顺序 先执行,是授权的前提 后执行,依赖认证结果
传递信息 身份凭证(密码、Token、证书) 权限策略、角色、属性
常见机制 JWT、OAuth2、Session、Basic Auth RBAC、ABAC、ACL、PBAC
失败结果 401 Unauthorized(未认证) 403 Forbidden(无权限)
在 GoBFF 中的位置 BFF 网关层或 API 网关 BFF 业务逻辑层或下游微服务
关键认知:在 HTTP 状态码中,401 表示“未认证”,而 403 表示“已认证但无权限”。很多团队在开发初期错误地将所有权限不足的情况都返回 401,这会导致客户端无法区分是凭证失效还是权限不足,增加调试复杂度。

下图展示了认证与授权在请求处理流程中的先后关系与职责边界:

认证与授权流程关系图 认证与授权在请求处理中的职责边界 客户端请求 认证模块 验证身份凭证 授权模块 检查操作权限 业务服务 认证失败 → 401 或授权失败 → 403 💡 认证通过后,请求中携带的身份信息(如用户ID、角色)被传递给授权模块;授权模块根据预定义的策略判断是否允许该操作。

3.2 常见权限模型概述

在微服务架构中,选择正确的权限模型直接影响系统的可维护性、灵活性和性能。目前业界最成熟的三种模型分别是:基于角色的访问控制(RBAC)、基于属性的访问控制(ABAC) 和 基于策略的访问控制(PBAC)。下表从多个维度对三者进行对比:

维度 RBAC(基于角色) ABAC(基于属性) PBAC(基于策略)
核心元素 用户、角色、权限 主体、资源、环境、操作 策略规则、条件、效果
粒度 粗粒度(角色级) 细粒度(属性级) 可粗可细(策略定义)
灵活性 中等,角色变更需管理员操作 高,属性组合可动态生成权限 高,策略可热加载
复杂度 低,易于理解和实施 高,属性管理复杂 中高,策略引擎设计复杂
适用场景 企业后台、CMS、标准 CRUD 金融、医疗、多租户系统 微服务网关、API 治理
Go 生态支持 casbin、kataras/role casbin(支持 ABAC)、openfga openfga、topaz、cerbos

3.3 RBAC:基于角色的访问控制

RBAC 是最经典的权限模型,其核心思想是将权限分配给角色,再将角色分配给用户。用户通过角色间接获得权限,而不是直接与权限挂钩。这种解耦方式极大地简化了权限管理。

RBAC 的核心要素:

  • 用户(User):系统的操作者,可以是一个人、一个服务账号或一个机器人。
  • 角色(Role):权限的集合,例如“管理员”、“编辑者”、“查看者”。
  • 权限(Permission):对某个资源执行某个操作的许可,例如“创建订单”、“删除用户”。
  • 会话(Session):用户激活的角色集合,一个用户可以在一次会话中激活多个角色。
GoBFF 实战建议:在 BFF 层使用 RBAC 时,建议将角色信息编码在 JWT 的 claims 中。BFF 网关解析 JWT 后,将角色列表传递给下游微服务,下游服务只需检查角色是否包含所需权限即可,无需重复查询用户-角色关系。

下面是一个典型的 RBAC 数据模型示例(Go 结构体定义):

// RBAC 核心模型
type User struct {
    ID       string   `json:"id"`
    Username string   `json:"username"`
    Roles    []string `json:"roles"` // 用户关联的角色ID列表
}

type Role struct {
    ID          string   `json:"id"`
    Name        string   `json:"name"`
    Permissions []string `json:"permissions"` // 角色拥有的权限标识列表
}

// 权限检查函数
func HasPermission(user *User, requiredPermission string) bool {
    // 实际项目中应从缓存或数据库加载角色-权限映射
    rolePermissions := map[string][]string{
        "admin": {"read", "write", "delete"},
        "editor": {"read", "write"},
        "viewer": {"read"},
    }
    for _, roleID := range user.Roles {
        if perms, ok := rolePermissions[roleID]; ok {
            for _, p := range perms {
                if p == requiredPermission {
                    return true
                }
            }
        }
    }
    return false
}

3.4 ABAC:基于属性的访问控制

ABAC 通过评估主体(用户)、资源、环境和操作的多维属性来决定是否允许访问。相比 RBAC 的“角色”单一维度,ABAC 可以表达更复杂的业务规则,例如:“只允许财务部门的经理在工作时间查看本部门的薪资报表”。

ABAC 的四类核心属性:

  • 主体属性(Subject Attributes):用户ID、部门、职位、安全等级、地理位置。
  • 资源属性(Resource Attributes):资源类型、所属部门、敏感级别、创建时间。
  • 环境属性(Environment Attributes):当前时间、IP 地址、设备类型、网络环境。
  • 操作属性(Action Attributes):操作类型(读/写/删除)、操作频率、操作方式。
性能陷阱:ABAC 每次请求都需要评估多个属性,如果属性值需要从数据库或远程服务实时获取,会显著增加请求延迟。在 GoBFF 架构中,建议将高频使用的属性(如用户部门、资源敏感级别)缓存到 BFF 层的内存中,避免每次授权都穿透到下游服务。

以下是一个使用 Casbin 实现 ABAC 的 Go 代码示例:

// ABAC 请求结构体
type ABACRequest struct {
    Subject   string // 用户ID
    Department string // 用户所属部门
    Resource  string // 资源ID
    ResDept   string // 资源所属部门
    Action    string // 操作
    Time      time.Time // 请求时间
}

// Casbin ABAC 匹配函数
func ABACMatchFunc(args ...interface{}) (interface{}, error) {
    req := args[0].(ABACRequest)
    rule := args[1].([]string)
    
    // 规则格式: [subject, department, resource, resDept, action, timeRange]
    // 例如: ["*", "finance", "*", "finance", "read", "09:00-18:00"]
    
    // 检查部门匹配
    if rule[1] != "*" && rule[1] != req.Department {
        return false, nil
    }
    // 检查资源部门匹配
    if rule[3] != "*" && rule[3] != req.ResDept {
        return false, nil
    }
    // 检查时间范围
    if rule[5] != "*" {
        // 解析时间范围并比较
        // ...
    }
    return true, nil
}

3.5 PBAC:基于策略的访问控制

PBAC 将权限决策抽象为独立的策略规则,通常使用声明式语言(如 Rego、CEL)或 YAML/JSON 配置文件来定义。PBAC 的核心优势在于策略与代码分离,权限变更无需修改业务代码,只需更新策略配置即可热生效。

PBAC 的典型架构组件:

  • 策略决策点(PDP):负责评估策略并做出授权决策的引擎。
  • 策略执行点(PEP):拦截请求并调用 PDP 获取决策结果,然后执行允许或拒绝。
  • 策略管理点(PAP):提供策略的创建、更新、删除管理界面。
  • 策略信息点(PIP):提供策略评估所需的属性数据源。

下图展示了 PBAC 在微服务架构中的组件交互关系:

PBAC 组件交互架构图 PBAC 在微服务中的组件交互架构 PEP 策略执行点 PDP 策略决策点 PAP 策略管理点 PIP 策略信息点 业务服务 请求决策 返回决策 下发策略 获取属性 客户端请求

PBAC 策略示例(使用 Rego 语言):

package authz

# 策略:允许用户删除自己创建的订单
default allow = false

allow {
    input.action == "delete"
    input.resource.type == "order"
    input.subject.id == input.resource.owner_id
}

# 策略:允许管理员在任何时间删除任何订单
allow {
    input.action == "delete"
    input.resource.type == "order"
    "admin" in input.subject.roles
}

# 策略:允许财务人员在09:00-18:00查看财务报表
allow {
    input.action == "read"
    input.resource.type == "financial_report"
    "finance" in input.subject.departments
    time_clock(input.time, "09:00", "18:00")
}

3.6 如何为 GoBFF 选择权限模型

在 GoBFF 架构中,权限模型的选择需要综合考虑 BFF 的定位、下游微服务的复杂度以及团队维护能力。以下是一份决策指南:

场景特征 推荐模型 理由
BFF 仅做路由聚合,业务逻辑简单 RBAC 实现成本低,角色清晰,易于审计
多租户 SaaS,租户间数据隔离严格 ABAC + 租户属性 通过租户ID属性实现天然隔离,无需为每个租户创建角色
BFF 对接多个异构下游,权限规则频繁变更 PBAC 策略热加载,无需重启 BFF 服务即可调整权限
混合场景:部分粗粒度、部分细粒度 RBAC + ABAC 混合 基础权限用 RBAC,特殊规则用 ABAC 补充
GoBFF 实战经验:在课程后续章节中,我们将基于 Casbin 实现一个支持 RBAC + ABAC 混合模式的权限中间件。Casbin 是 Go 生态中最成熟的权限库之一,它原生支持多种模型,并且可以无缝集成到 Gin、Echo 等主流 Web 框架中。我们将演示如何在 BFF 网关层用不到 50 行代码完成权限校验的嵌入。