Skip to content

授权总览

授权回答"你能做什么"——消费认证产出的 context.user,根据端点声明的策略决定放行、401 还是 403。

适用场景

  • 需要基于登录态、角色、声明或自定义条件控制接口访问
  • 希望端点只声明"需要什么权限",判定规则由授权系统集中管理
  • 多认证方案并存时,需要为不同接口指定不同的认证方式

核心问题

授权是请求管道中介于认证和业务逻辑之间的权限判定层

请求 → useAuthentication() → useAuthorization() → 业务逻辑
           ↑                        ↑
    填 context.user          读 context.user + Endpoint.metadata
    (认证产出身份)           (授权判定权限)

                    ┌─────────────┼─────────────┐
                    ▼             ▼             ▼
                 放行          401            403
                            (未认证)    (已认证但权限不够)

它与认证的分工明确:

维度认证 Authentication授权 Authorization
问题你是谁?你能做什么?
输入Token / Cookie / 自定义凭证context.user + Endpoint.metadata
输出ClaimsPrincipal放行 / challenge(401) / forbid(403)
失败行为不拦截,请求继续往下走未认证 → 401,已认证无权限 → 403

核心流程

授权中间件 useAuthorization() 分五步执行:

请求进入 AuthorizationMiddleware


① 解析策略:从 Endpoint.metadata 提取 IAuthorizeData,组合成 AuthorizationPolicy

  ├── 无策略且无 fallbackPolicy → 直接 next(),跳过授权

  └── 解析出策略 → 继续


② 执行认证:按策略指定的 authenticationSchemes 调 context.authenticate()


③ AllowAnonymous?→ next(),授权失败也不拦截


④ 执行授权:遍历所有 IAuthorizationHandler 处理 requirements

        ├── pendingRequirements 全部清空 → 放行

        └── 有残留 →
              ├── 未认证 → challenge() → 401
              └── 已认证 → forbid() → 403

深入执行细节

每一步的源码级拆解见 授权策略 的核心流程章节。

模块地图

授权体系包含三个核心概念,按从外到内的层次组织:

层次概念说明详细文档
端点层IAuthorizeData端点通过 requireAuthorization() / allowAnonymous() 声明授权要求授权策略
策略层AuthorizationPolicy一组 requirements + authenticationSchemes 的组合,可通过名称引用授权策略
判定层IAuthorizationRequirement + IAuthorizationHandler具体的权限判定逻辑,内置 6 种 + 可自定义自定义授权要求

架构组件速查

组件角色
AuthorizationMiddleware请求管道的授权入口,驱动策略解析和执行
IAuthorizationService核心授权逻辑:收束 handler、执行判定、评估结果
AuthorizationPolicy策略对象:包含 requirements 列表和 authenticationSchemes
AuthorizationPolicyBuilder策略的流式构建器:requireClaim() / requireRole() / requireAuthenticatedUser()
IAuthorizationRequirement授权要求标记接口——"需要满足什么条件"
IAuthorizationHandler / AuthorizationHandler<T>授权处理器——"条件是否满足"的判断逻辑
AuthorizationHandlerContext处理器上下文:维护 pendingRequirementssucceed() / fail() 状态
IPolicyEvaluator策略执行器:先调认证、再调授权,输出 PolicyAuthorizationResult
IAuthorizationMiddlewareResultHandler结果处理器:succeeded → next()、challenged → 401、forbidden → 403
IAuthorizationPolicyProvider策略提供器:按名称或默认/回退规则获取策略实例

内置的授权要求

框架提供了 6 种内置 requirement,覆盖常见的权限判定场景:

Requirement用途示例
DenyAnonymousAuthorizationRequirement拒绝匿名用户,要求登录requireAuthenticatedUser()
RolesAuthorizationRequirement按角色校验requireRole(["admin", "manager"])
ClaimsAuthorizationRequirement按声明值校验requireClaim("age") { age => age.toInt64() >= 18 }
NameAuthorizationRequirement按用户名校验requireUserName("spire")
AssertionRequirement自定义断言函数传入 (context) => Bool 委托
OperationAuthorizationRequirement操作名称标记资源级授权场景

所有内置 requirement 都采用自处理模式——requirement 自身同时作为 handler,无需额外 DI 注册。

三种策略层次

端点显式指定策略 → requireAuthorization(["AdminOnly"])

        ▼ 未指定策略名称
defaultPolicy(默认:只要登录即可)

        ▼ 端点无任何授权元数据
fallbackPolicy(默认:None,即跳过授权)
策略默认值行为
defaultPolicyrequireAuthenticatedUser()requireAuthorization() 不带参数时使用
fallbackPolicyNone所有未标注 requireAuthorization() 的端点统一应用的策略
命名策略无,需手动添加通过 options.addPolicy("名称") 注册,端点上按名称引用

应用授权到端点

授权策略最终要挂到端点上才生效。MiniAPI 和 MVC 用了不同的语法,但原理相同——都是在 Endpoint.metadata 中写入 IAuthorizeDataIAllowAnonymous

MiniAPI:链式调用

在端点或分组上直接调用 requireAuthorization() / allowAnonymous()

cangjie
// 单个端点
app.mapGet("/admin") { ctx => ctx.response.write("admin") }
    .requireAuthorization(["AdminOnly"])

// 分组统一授权(组内所有端点自动继承)
let admin = app.mapGroup("/admin")
admin.requireAuthorization(["AdminOnly"])
admin.mapGet("/users") { ctx => ctx.response.write("users") }    // 自动继承
admin.mapGet("/logs") { ctx => ctx.response.write("logs") }      // 自动继承

// 分组内的个别端点豁免
admin.mapGet("/public") { ctx => ctx.response.write("public") }
    .allowAnonymous()

allowAnonymous() 优先级最高——授权中间件先检查 IAllowAnonymous,发现就直接放行。

MVC:注解

在控制器或动作上标注 @Authorize / @AllowAnonymous

cangjie
// 控制器级——所有动作继承
@Authorize
public class AdminController <: Controller {
    @HttpGet
    public func users(): String { "users" }        // 继承 @Authorize

    @HttpGet
    public func logs(): String { "logs" }          // 继承 @Authorize

    @HttpGet
    @AllowAnonymous
    public func notice(): String { "notice" }      // 覆盖,公开访问
}

// 全局默认授权——在 mapControllers 时统一加锁
app.mapControllers().requireAuthorization(["default"])

殊途同归

无论 MiniAPI 的 requireAuthorization() 还是 MVC 的 @Authorize,最终都是往 Endpoint.metadata 写入元数据:

MiniAPI:  .requireAuthorization(["AdminOnly"])  ─┐
                                                  ├──→  Endpoint.metadata
MVC:      @Authorize                             ─┘     ├── IAuthorizeData("AdminOnly")
                                                        └── IAllowAnonymous

授权中间件只读 Endpoint.metadata,不关心元数据是 MiniAPI 链式调用写入的还是 MVC 注解扫描写入的。两种风格可以混用,判定逻辑完全一致。

推荐阅读顺序

  1. 授权策略 — 了解策略如何从端点元数据构建,如何添加命名策略
  2. 自定义授权要求 — 当内置 requirement 不够用时,自己实现判定逻辑
  3. 认证总览 — 理解认证产出如何被授权消费

当前边界

  • 已落地:完整的策略+requirement+handler 模型,6 种内置 requirement,两种自定义模式(自处理/分离),多认证方案指定,策略缓存
  • 已支持但文档未展开IAuthorizationPolicyProvider 完全自定义策略源、IAuthorizationMiddlewareResultHandler 自定义 401/403 响应格式、IAuthorizationEvaluator 自定义结果评估规则、AuthorizationResourceHandler<TReq, TRes> 资源级授权基类
  • 当前未覆盖:资源级授权(Resource-based Authorization)的完整落地案例

相关专题