name: 授权模型 description: 全面的授权指导,涵盖RBAC、ABAC、ACL、ReBAC和策略即代码模式。用于设计权限系统、实现访问控制或选择授权策略。 allowed-tools: Read, Glob, Grep, Task
授权模型技能
概述
这个技能提供关于授权模型和访问控制模式的全面指导。授权确定已认证用户在系统内可以执行的操作。
关键原则: 授权应该是声明式的、集中化的和可审计的。
何时使用此技能
- 从零开始设计权限系统
- 在RBAC、ABAC、ACL或ReBAC之间选择
- 使用OPA实现策略即代码
- 从简单角色检查迁移到细粒度授权
- 实现最小权限原则
- 设计多租户授权
- 构建Zanzibar风格的权限系统
授权模型比较
| 模型 | 最佳适用 | 复杂性 | 可扩展性 | 灵活性 |
|---|---|---|---|---|
| ACL | 文件系统、简单资源 | 低 | 中 | 低 |
| RBAC | 企业应用、清晰的工作角色 | 中 | 高 | 中 |
| ABAC | 复杂策略、动态规则 | 高 | 高 | 高 |
| ReBAC | 社交图、文档共享 | 中-高 | 非常高 | 高 |
快速决策树
需要授权模型?
├── 简单的资源所有权?
│ └── ACL(访问控制列表)
├── 清晰的组织角色?
│ └── RBAC(基于角色的访问控制)
├── 复杂的、上下文相关的规则?
│ └── ABAC(基于属性的访问控制)
└── 基于关系的访问(共享、层次结构)?
└── ReBAC(基于关系的访问控制)
基于角色的访问控制(RBAC)
核心概念
/// <summary>
/// RBAC的细粒度权限。
/// </summary>
[Flags]
public enum Permission
{
None = 0,
Read = 1,
Create = 2,
Update = 4,
Delete = 8,
Admin = 16,
Approve = 32,
Publish = 64,
// 常见组合
ReadWrite = Read | Update,
Editor = Read | Create | Update,
FullAccess = Read | Create | Update | Delete | Admin
}
/// <summary>
/// 具有关联权限的角色。
/// </summary>
public sealed record Role(string Name, Permission Permissions, string Description = "");
/// <summary>
/// 标准角色定义。
/// </summary>
public static class StandardRoles
{
public static readonly Role Viewer = new("viewer", Permission.Read, "只读访问");
public static readonly Role Editor = new("editor", Permission.Editor, "可以创建和编辑内容");
public static readonly Role Admin = new("admin", Permission.FullAccess, "完全管理访问");
public static readonly IReadOnlyDictionary<string, Role> All = new Dictionary<string, Role>
{
[Viewer.Name] = Viewer,
[Editor.Name] = Editor,
[Admin.Name] = Admin
};
}
RBAC实现
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
/// <summary>
/// 简单的RBAC授权服务。
/// </summary>
public sealed class RbacAuthorizer
{
private readonly Dictionary<string, HashSet<string>> _userRoles = new();
public void AssignRole(string userId, string role)
{
if (!_userRoles.TryGetValue(userId, out var roles))
{
roles = new HashSet<string>();
_userRoles[userId] = roles;
}
roles.Add(role);
}
public bool HasPermission(string userId, Permission permission)
{
if (!_userRoles.TryGetValue(userId, out var userRoles))
return false;
foreach (var roleName in userRoles)
{
if (StandardRoles.All.TryGetValue(roleName, out var role) &&
role.Permissions.HasFlag(permission))
{
return true;
}
}
return false;
}
public bool HasRole(string userId, string role) =>
_userRoles.TryGetValue(userId, out var roles) && roles.Contains(role);
}
/// <summary>
/// ASP.NET Core权限授权需求。
/// </summary>
public sealed class PermissionRequirement(Permission permission) : IAuthorizationRequirement
{
public Permission Permission { get; } = permission;
}
/// <summary>
/// 基于权限的授权处理器。
/// </summary>
public sealed class PermissionHandler(RbacAuthorizer authorizer) : AuthorizationHandler<PermissionRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionRequirement requirement)
{
var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
if (userId is not null && authorizer.HasPermission(userId, requirement.Permission))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
// 使用属性
[Authorize(Policy = "RequireCreate")]
[HttpPost("articles")]
public IActionResult CreateArticle([FromBody] ArticleDto article)
{
// 只有具有CREATE权限的用户可以访问
return Ok();
}
分层RBAC
/// <summary>
/// 支持继承的角色。
/// </summary>
public sealed class HierarchicalRole(
string name,
Permission directPermissions,
HierarchicalRole? parent = null)
{
public string Name { get; } = name;
public HierarchicalRole? Parent { get; } = parent;
/// <summary>
/// 获取所有权限,包括从父角色继承的。
/// </summary>
public Permission AllPermissions
{
get
{
var permissions = directPermissions;
var current = Parent;
while (current is not null)
{
permissions |= current.AllPermissions;
current = current.Parent;
}
return permissions;
}
}
}
// 角色层次:admin > editor > viewer
var viewerRole = new HierarchicalRole("viewer", Permission.Read);
var editorRole = new HierarchicalRole("editor", Permission.Create | Permission.Update, viewerRole);
var adminRole = new HierarchicalRole("admin", Permission.Delete | Permission.Admin, editorRole);
// adminRole.AllPermissions 包括从父角色继承的所有权限
基于属性的访问控制(ABAC)
核心概念
using System.Collections.Immutable;
/// <summary>
/// 访问决策的上下文。
/// </summary>
public sealed record AccessRequest(
ImmutableDictionary<string, object> Subject, // 谁在请求
ImmutableDictionary<string, object> Resource, // 他们在访问什么
string Action, // 他们想做什么
ImmutableDictionary<string, object> Environment // 上下文(时间、位置等)
)
{
public T GetSubjectAttribute<T>(string key, T defaultValue = default!) =>
Subject.TryGetValue(key, out var value) && value is T typed ? typed : defaultValue;
public T GetResourceAttribute<T>(string key, T defaultValue = default!) =>
Resource.TryGetValue(key, out var value) && value is T typed ? typed : defaultValue;
public T GetEnvironmentAttribute<T>(string key, T defaultValue = default!) =>
Environment.TryGetValue(key, out var value) && value is T typed ? typed : defaultValue;
}
/// <summary>
/// 策略效果类型。
/// </summary>
public enum PolicyEffect { Permit, Deny }
/// <summary>
/// 基于属性的策略评估。
/// </summary>
public sealed class AbacPolicy(
string name,
Func<AccessRequest, bool> condition,
PolicyEffect effect = PolicyEffect.Permit)
{
public string Name { get; } = name;
/// <summary>
/// 如果条件匹配返回效果,否则返回null。
/// </summary>
public PolicyEffect? Evaluate(AccessRequest request) =>
condition(request) ? effect : null;
}
ABAC策略示例
// 策略:只有经理可以批准超过1000美元的支出
var managerApprovalPolicy = new AbacPolicy(
name: "manager_approval",
condition: req =>
req.Action == "approve" &&
req.GetResourceAttribute<string>("type") == "expense" &&
req.GetResourceAttribute<decimal>("amount") > 1000 &&
req.GetSubjectAttribute<string>("role") == "manager"
);
// 策略:用户只能访问自己部门的数据
var departmentIsolationPolicy = new AbacPolicy(
name: "department_isolation",
condition: req =>
req.GetSubjectAttribute<string>("department") ==
req.GetResourceAttribute<string>("department")
);
// 策略:非工作时间无访问
var businessHoursPolicy = new AbacPolicy(
name: "business_hours",
condition: req =>
{
var now = DateTime.Now;
var hour = now.Hour;
var dayOfWeek = now.DayOfWeek;
return hour >= 9 && hour <= 17 &&
dayOfWeek != DayOfWeek.Saturday &&
dayOfWeek != DayOfWeek.Sunday;
}
);
// 策略:拒绝来自不信任网络的访问
var networkPolicy = new AbacPolicy(
name: "trusted_network",
condition: req =>
req.GetEnvironmentAttribute<string>("ip_address", "")
.StartsWith("10.0.", StringComparison.Ordinal)
);
ABAC策略引擎
/// <summary>
/// 策略决策点(PDP)使用拒绝覆盖算法。
/// </summary>
public sealed class AbacEngine(PolicyEffect defaultEffect = PolicyEffect.Deny)
{
private readonly List<AbacPolicy> _policies = [];
public void AddPolicy(AbacPolicy policy) => _policies.Add(policy);
/// <summary>
/// 评估所有策略。拒绝覆盖组合算法。
/// </summary>
public bool Evaluate(AccessRequest request)
{
var permitFound = false;
foreach (var policy in _policies)
{
var effect = policy.Evaluate(request);
if (effect == PolicyEffect.Deny)
{
return false; // 显式拒绝总是胜出
}
if (effect == PolicyEffect.Permit)
{
permitFound = true;
}
}
return permitFound || defaultEffect == PolicyEffect.Permit;
}
}
// 使用
var engine = new AbacEngine();
engine.AddPolicy(departmentIsolationPolicy);
engine.AddPolicy(businessHoursPolicy);
var request = new AccessRequest(
Subject: ImmutableDictionary.CreateRange(new Dictionary<string, object>
{
["user_id"] = "123",
["department"] = "engineering",
["role"] = "developer"
}),
Resource: ImmutableDictionary.CreateRange(new Dictionary<string, object>
{
["id"] = "doc-456",
["department"] = "engineering",
["type"] = "document"
}),
Action: "read",
Environment: ImmutableDictionary.CreateRange(new Dictionary<string, object>
{
["ip_address"] = "10.0.1.50",
["time"] = DateTime.UtcNow
})
);
if (engine.Evaluate(request))
{
// 访问授予
}
访问控制列表(ACL)
简单ACL实现
/// <summary>
/// Unix风格的ACL权限。
/// </summary>
[Flags]
public enum AclPermission
{
None = 0,
Read = 1,
Write = 2,
Execute = 4,
Delete = 8,
Admin = 16,
// 常见组合
ReadWrite = Read | Write,
Full = Read | Write | Execute | Delete | Admin
}
/// <summary>
/// ACL条目的主体类型。
/// </summary>
public enum PrincipalType { User, Group }
/// <summary>
/// 访问控制条目。
/// </summary>
public sealed class AclEntry(string principal, AclPermission permissions, PrincipalType principalType = PrincipalType.User)
{
public string Principal { get; } = principal;
public AclPermission Permissions { get; set; } = permissions;
public PrincipalType PrincipalType { get; } = principalType;
}
/// <summary>
/// 资源的访问控制列表。
/// </summary>
public sealed class Acl(string resourceId, string owner)
{
public string ResourceId { get; } = resourceId;
public string Owner { get; } = owner;
private readonly Dictionary<string, AclEntry> _entries = new();
/// <summary>
/// 授予权限给主体。
/// </summary>
public void Grant(string principal, AclPermission permissions, PrincipalType principalType = PrincipalType.User)
{
if (_entries.TryGetValue(principal, out var entry))
{
entry.Permissions |= permissions;
}
else
{
_entries[principal] = new AclEntry(principal, permissions, principalType);
}
}
/// <summary>
/// 从主体撤销权限。
/// </summary>
public void Revoke(string principal, AclPermission permissions)
{
if (_entries.TryGetValue(principal, out var entry))
{
entry.Permissions &= ~permissions;
if (entry.Permissions == AclPermission.None)
{
_entries.Remove(principal);
}
}
}
/// <summary>
/// 检查主体是否有权限。
/// </summary>
public bool Check(string principal, AclPermission permission, ISet<string>? userGroups = null)
{
// 所有者有完全访问
if (principal == Owner)
{
return true;
}
// 检查直接用户条目
if (_entries.TryGetValue(principal, out var entry) &&
entry.Permissions.HasFlag(permission))
{
return true;
}
// 检查组条目
if (userGroups is not null)
{
foreach (var group in userGroups)
{
if (_entries.TryGetValue(group, out var groupEntry) &&
groupEntry.PrincipalType == PrincipalType.Group &&
groupEntry.Permissions.HasFlag(permission))
{
return true;
}
}
}
return false;
}
}
ACL使用示例
// 创建文档的ACL
var docAcl = new Acl(resourceId: "doc-123", owner: "alice");
// 授予权限
docAcl.Grant("bob", AclPermission.ReadWrite);
docAcl.Grant("engineering", AclPermission.Read, PrincipalType.Group);
docAcl.Grant("charlie", AclPermission.Read);
// 检查权限
docAcl.Check("alice", AclPermission.Delete); // True (所有者)
docAcl.Check("bob", AclPermission.Write); // True (显式授予)
docAcl.Check("bob", AclPermission.Delete); // False (未授予)
docAcl.Check("dave", AclPermission.Read,
userGroups: new HashSet<string> { "engineering" }); // True (组成员资格)
基于关系的访问控制(ReBAC)
Zanzibar风格模型
/// <summary>
/// Zanzibar风格的关系元组(对象、关系、主体)。
/// </summary>
public readonly record struct Relationship(
string ObjectType,
string ObjectId,
string Relation,
string SubjectType,
string SubjectId,
string? SubjectRelation = null) // 用于用户集
{
public override string ToString()
{
var subject = $"{SubjectType}:{SubjectId}";
if (SubjectRelation is not null)
{
subject += $"#{SubjectRelation}";
}
return $"{ObjectType}:{ObjectId}#{Relation}@{subject}";
}
}
/// <summary>
/// 简单的ReBAC实现(受Zanzibar启发)。
/// </summary>
public sealed class ReBac
{
// 存储关系: (object_type, object_id, relation) -> 主体集合
private readonly Dictionary<(string ObjectType, string ObjectId, string Relation),
HashSet<(string SubjectType, string SubjectId, string? SubjectRelation)>> _tuples = new();
// 关系定义与计算关系
// object_type -> relation -> 父关系集合用于继承
private readonly Dictionary<string, Dictionary<string, HashSet<string>>> _relationConfig = new();
/// <summary>
/// 定义关系及其继承。
/// </summary>
public void DefineRelation(string objectType, string relation, IEnumerable<string>? inheritsFrom = null)
{
if (!_relationConfig.TryGetValue(objectType, out var relations))
{
relations = new Dictionary<string, HashSet<string>>();
_relationConfig[objectType] = relations;
}
relations[relation] = inheritsFrom?.ToHashSet() ?? [];
}
/// <summary>
/// 写入关系元组。
/// </summary>
public void Write(Relationship rel)
{
var key = (rel.ObjectType, rel.ObjectId, rel.Relation);
if (!_tuples.TryGetValue(key, out var subjects))
{
subjects = [];
_tuples[key] = subjects;
}
subjects.Add((rel.SubjectType, rel.SubjectId, rel.SubjectRelation));
}
/// <summary>
/// 删除关系元组。
/// </summary>
public void Delete(Relationship rel)
{
var key = (rel.ObjectType, rel.ObjectId, rel.Relation);
if (_tuples.TryGetValue(key, out var subjects))
{
subjects.Remove((rel.SubjectType, rel.SubjectId, rel.SubjectRelation));
}
}
/// <summary>
/// 检查主体是否与对象有关系。
/// </summary>
public bool Check(string objectType, string objectId, string relation,
string subjectType, string subjectId)
{
var key = (objectType, objectId, relation);
// 直接检查
if (_tuples.TryGetValue(key, out var subjects) &&
subjects.Contains((subjectType, subjectId, null)))
{
return true;
}
// 检查继承的关系
if (_relationConfig.TryGetValue(objectType, out var relations) &&
relations.TryGetValue(relation, out var inherited))
{
foreach (var parentRelation in inherited)
{
if (Check(objectType, objectId, parentRelation, subjectType, subjectId))
{
return true;
}
}
}
// 检查用户集重写(例如,folder:123#viewer@document:456#parent)
if (_tuples.TryGetValue(key, out var tupleSubjects))
{
foreach (var (subjType, subjId, subjRel) in tupleSubjects)
{
if (subjRel is not null)
{
// 这是一个用户集 - 检查用户在引用对象上是否有该关系
if (Check(subjType, subjId, subjRel, subjectType, subjectId))
{
return true;
}
}
}
}
return false;
}
}
ReBAC使用示例(Google Drive风格)
// 初始化ReBAC
var rebac = new ReBac();
// 定义关系层次
// 编辑者也可以查看(编辑者继承自查看者)
rebac.DefineRelation("document", "viewer", null);
rebac.DefineRelation("document", "editor", ["viewer"]);
rebac.DefineRelation("document", "owner", ["editor"]);
rebac.DefineRelation("folder", "viewer", null);
rebac.DefineRelation("folder", "editor", ["viewer"]);
rebac.DefineRelation("folder", "owner", ["editor"]);
// 文件夹查看者是文档查看者(文档从父文件夹继承)
rebac.DefineRelation("document", "parent", null);
// 创建关系
// Alice拥有文件夹"projects"
rebac.Write(new Relationship("folder", "projects", "owner", "user", "alice"));
// Bob是文件夹"projects"的编辑者
rebac.Write(new Relationship("folder", "projects", "editor", "user", "bob"));
// 文档"spec"在文件夹"projects"中
// 任何可以查看文件夹的人都可以查看文档
rebac.Write(new Relationship("document", "spec", "parent", "folder", "projects",
SubjectRelation: "viewer"));
// Charlie有直接查看文档的访问权限
rebac.Write(new Relationship("document", "spec", "viewer", "user", "charlie"));
// 检查权限
rebac.Check("folder", "projects", "owner", "user", "alice"); // True
rebac.Check("folder", "projects", "viewer", "user", "alice"); // True (owner->editor->viewer)
rebac.Check("folder", "projects", "editor", "user", "bob"); // True
rebac.Check("document", "spec", "viewer", "user", "bob"); // True (folder editor->viewer)
rebac.Check("document", "spec", "editor", "user", "charlie"); // False (只有查看者)
策略即代码与Open Policy Agent(OPA)
Rego策略基础
# policy.rego
package authz
import future.keywords.if
import future.keywords.in
# 默认拒绝
default allow := false
# 如果用户有所需权限则允许
allow if {
user_has_permission[input.action]
}
# 基于角色的用户权限
user_has_permission[permission] if {
some role in input.user.roles
some permission in role_permissions[role]
}
# 角色到权限映射
role_permissions := {
"admin": ["read", "write", "delete", "admin"],
"editor": ["read", "write"],
"viewer": ["read"],
}
# 资源特定规则
allow if {
input.action == "read"
input.resource.public == true
}
# 所有者总是可以访问其资源
allow if {
input.resource.owner == input.user.id
}
.NET中的OPA集成
using System.Text.Json;
using System.Text.Json.Serialization;
/// <summary>
/// Open Policy Agent客户端。
/// </summary>
public sealed class OpaClient(HttpClient httpClient, string opaUrl = "http://localhost:8181") : IDisposable
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
/// <summary>
/// 查询OPA以获取策略决策。
/// </summary>
public async Task<bool> CheckAsync(string policyPath, object inputData, CancellationToken cancellationToken = default)
{
var url = $"{opaUrl}/v1/data/{policyPath}";
var request = new OpaRequest(inputData);
var response = await httpClient.PostAsJsonAsync(url, request, JsonOptions, cancellationToken);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<OpaResponse<bool>>(JsonOptions, cancellationToken);
return result?.Result ?? false;
}
/// <summary>
/// 运行任意Rego查询。
/// </summary>
public async Task<JsonElement?> QueryAsync(string query, object inputData, CancellationToken cancellationToken = default)
{
var url = $"{opaUrl}/v1/query";
var request = new { query, input = inputData };
var response = await httpClient.PostAsJsonAsync(url, request, JsonOptions, cancellationToken);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<OpaResponse<JsonElement>>(JsonOptions, cancellationToken);
return result?.Result;
}
public void Dispose() => httpClient.Dispose();
private sealed record OpaRequest([property: JsonPropertyName("input")] object Input);
private sealed record OpaResponse<T>([property: JsonPropertyName("result")] T? Result);
}
// 使用
using var httpClient = new HttpClient();
var opa = new OpaClient(httpClient);
var inputData = new
{
user = new
{
id = "alice",
roles = new[] { "editor" }
},
action = "write",
resource = new
{
id = "doc-123",
owner = "bob",
@public = false
}
};
if (await opa.CheckAsync("authz/allow", inputData))
{
// 访问授予
}
使用ABAC策略的OPA
# abac_policy.rego
package abac
import future.keywords.if
import future.keywords.in
default allow := false
# 基于时间的访问
allow if {
input.action == "read"
is_business_hours
user_in_same_department
}
is_business_hours if {
time.hour(time.now_ns()) >= 9
time.hour(time.now_ns()) <= 17
time.weekday(time.now_ns()) < 5
}
user_in_same_department if {
input.user.department == input.resource.department
}
# 支出批准规则
allow if {
input.action == "approve"
input.resource.type == "expense"
can_approve_amount
}
can_approve_amount if {
input.resource.amount <= 1000
"employee" in input.user.roles
}
can_approve_amount if {
input.resource.amount <= 10000
"manager" in input.user.roles
}
can_approve_amount if {
"director" in input.user.roles
}
授权库和工具
比较
| 工具 | 模型 | 语言 | 最佳适用 |
|---|---|---|---|
| OPA | ABAC/策略 | Rego | Kubernetes、微服务 |
| Casbin | RBAC/ABAC/ACL | 多语言 | 通用目的 |
| Oso | ABAC/ReBAC | Polar | 应用嵌入 |
| SpiceDB | ReBAC (Zanzibar) | gRPC | 大规模权限 |
| Cerbos | ABAC | YAML | 云原生应用 |
Casbin快速入门
using Casbin;
// 定义模型 (model.conf)
/*
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
*/
// 创建执行器
var enforcer = new Enforcer("model.conf", "policy.csv");
// 检查权限
if (enforcer.Enforce("alice", "data1", "read"))
{
// 访问授予
}
// 动态添加策略
await enforcer.AddPolicyAsync("bob", "data2", "write");
await enforcer.AddGroupingPolicyAsync("alice", "admin");
// 对于ASP.NET Core集成,使用Casbin.AspNetCore
// services.AddCasbinAuthorization(options =>
// {
// options.DefaultModelPath = "model.conf";
// options.DefaultPolicyPath = "policy.csv";
// });
最佳实践
设计原则
- 最小权限原则:授予必要的最小权限
- 职责分离:敏感操作需要多方参与
- 深度防御:在多个级别层叠授权检查
- 安全失败:当授权状态不明确时拒绝访问
- 集中逻辑:单一策略决策点(PDP)
- 审计一切:记录所有授权决策
实现指南
// 好:集中化授权
public interface IAuditLogger
{
Task LogAsync(string userId, string resourceId, string action, string decision, object? context);
}
/// <summary>
/// 集中化授权服务 - 所有决策的单一入口点。
/// </summary>
public sealed class AuthorizationService(AbacEngine engine, IAuditLogger auditLogger)
{
/// <summary>
/// 所有授权决策的单一入口点。
/// </summary>
public async Task<bool> AuthorizeAsync(AccessRequest request)
{
var decision = engine.Evaluate(request);
// 总是审计
await auditLogger.LogAsync(
userId: request.GetSubjectAttribute<string>("user_id") ?? "unknown",
resourceId: request.GetResourceAttribute<string>("id") ?? "unknown",
action: request.Action,
decision: decision ? "permit" : "deny",
context: request.Environment
);
return decision;
}
}
// 差:分散的授权检查
public Document? GetDocument(string docId, User user)
{
var doc = _repository.GetById(docId);
// 授权逻辑到处重复 - 避免此模式!
if (user.Role == "admin" || doc?.OwnerId == user.Id)
{
return doc;
}
return null;
}
常见陷阱
| 陷阱 | 问题 | 解决方案 |
|---|---|---|
| 硬编码角色 | 不灵活,难以更改 | 使用基于权限的检查 |
| 缺少负面测试 | 虚假安全感 | 明确测试拒绝案例 |
| 仅客户端 | 容易被绕过 | 总是在服务器端强制执行 |
| 过于复杂策略 | 难以审计 | 保持策略简单、可组合 |
| 无审计跟踪 | 无法调查事件 | 记录所有决策 |
相关技能
authentication-patterns- 授权前验证身份api-security- 在API边界应用授权zero-trust- 永不信任、总是验证的架构secure-coding- 防止授权绕过漏洞
参考资料
深度探讨:
安全清单
设计阶段
- [ ] 基于需求选择授权模型
- [ ] 应用最小权限原则
- [ ] 记录角色/权限
- [ ] 识别边界情况(继承、委托)
实现阶段
- [ ] 授权集中化(单一PDP)
- [ ] 服务器端强制执行
- [ ] 所有端点一致授权检查
- [ ] 实现审计日志
测试阶段
- [ ] 正面测试(允许的访问有效)
- [ ] 负面测试(拒绝的访问被阻止)
- [ ] 特权提升测试
- [ ] 角色层次测试
运营阶段
- [ ] 定期权限审查
- [ ] 删除未使用的角色/权限
- [ ] 监控审计日志
- [ ] 授权失败的应急响应计划