1. 权限验证概述
1.1 为什么需要在业务模块边界做权限验证
在微服务与模块化架构中,每个业务模块都是一个独立的自治单元。模块边界是数据与逻辑的“最后一道防线”。不在边界做权限验证,会导致以下风险:
- 越权访问:用户通过合法身份调用模块接口,但操作了不属于自己权限范围的数据(如普通用户删除他人订单)。
- 水平越权:攻击者通过修改请求中的用户ID或角色ID,访问同级别其他用户的资源。
- 垂直越权:低权限用户通过绕过前端校验,直接调用高权限接口(如普通员工调用管理员审批接口)。
- 数据泄露:模块内部未做权限过滤,导致批量查询接口返回了不应暴露的敏感字段。
核心原则:权限验证必须发生在业务逻辑执行之前,且不能依赖前端或网关的校验结果。模块边界是信任的起点,也是安全的终点。
在Go语言实践中,模块边界通常对应以下位置:
- gRPC/HTTP 路由中间件
- 服务层方法的入口
- 数据访问层(Repository)的查询/写入前置检查
1.2 常见权限模型对比
业界主流的权限模型有四种:DAC、MAC、RBAC、ABAC。它们在粒度、灵活性、管理复杂度上各有优劣。
| 模型 | 全称 | 核心思想 | 粒度 | 适用场景 | Go实现复杂度 |
|---|---|---|---|---|---|
| DAC | 自主访问控制 | 资源所有者自行决定谁可以访问 | 资源级 | 文件系统、个人文档 | 低 |
| MAC | 强制访问控制 | 系统根据安全标签强制控制 | 标签级 | 军事、政府机密系统 | 高 |
| RBAC | 基于角色的访问控制 | 用户→角色→权限 | 角色级 | 企业管理系统、SaaS平台 | 中 |
| ABAC | 基于属性的访问控制 | 根据用户、资源、环境属性动态计算 | 属性级 | 复杂业务规则、IoT、金融风控 | 高 |
选型建议:大多数Go业务模块首选RBAC,因为它平衡了灵活性与实现成本。当业务规则需要根据时间、地点、设备等动态条件判断时,可升级为ABAC。
1.3 权限模型关系图
下图展示了四种权限模型在“用户→权限”映射路径上的核心差异:
1.4 各模型在Go模块中的典型实现
以下代码片段展示了RBAC在Go服务层边界的基本验证模式:
// 模块边界权限验证中间件(RBAC模式)
func RBACMiddleware(requiredRole string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 从上下文中提取用户角色(由认证中间件注入)
userRole, ok := r.Context().Value("role").(string)
if !ok {
http.Error(w, "missing role in context", http.StatusForbidden)
return
}
// 角色层级检查(admin > manager > user)
if !hasRoleHierarchy(userRole, requiredRole) {
http.Error(w, "insufficient permissions", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
}
// 角色层级比较(简化示例)
func hasRoleHierarchy(current, required string) bool {
hierarchy := map[string]int{
"admin": 3,
"manager": 2,
"user": 1,
}
return hierarchy[current] >= hierarchy[required]
}
常见陷阱:不要在模块边界只校验“是否登录”,而忽略“是否有权操作具体资源”。例如,用户A可以访问订单列表,但不应看到用户B的订单。边界验证必须同时包含身份认证和资源授权两层。
1.5 模块边界权限验证的架构位置
下图展示了权限验证在Go业务模块中的典型部署位置:
1.6 本章小结
- 模块边界权限验证是防止越权访问的最后一道防线,不能依赖前端或网关。
- 四种主流模型(DAC、MAC、RBAC、ABAC)在粒度、灵活性和实现成本上差异显著。
- Go业务模块推荐以RBAC为起点,在复杂场景下引入ABAC补充动态策略。
- 权限验证应作为中间件或装饰器,在业务逻辑执行前完成,保持业务代码的纯净。