Skip to content

认证总览

认证回答"你是谁"——把请求中的凭证(Token、Cookie、自定义协议)还原成用户身份,写入 HttpContext.user

核心问题

认证是请求管道的身份识别环节。它不负责判断"你能不能访问"(那是授权的活),只负责搞清楚"你是谁"。

一个典型的请求处理流程:

请求 → useRouting() → useAuthentication() → useAuthorization() → 业务逻辑
                           ↑                        ↑
                    填 context.user          读 context.user
                    (认证的产出)           (授权消费它)

认证中间件跑完,请求上就有了 context.user(一个 ClaimsPrincipal)。至于这个用户能不能访问当前接口,交给后面的授权中间件决定。

认证的完整流程

认证中间件 useAuthentication() 执行三步:

  1. 检查是否有请求级处理器(如 OIDC 登录跳转)——有就截断管道,直接返回
  2. 取默认认证方案,创建 handler,执行 authenticate()——handler 从请求中读取凭证(Token / Cookie / 自定义头),校验后生成 ClaimsPrincipal,写入 context.user
  3. next(context)——无论认证成功还是失败,请求都继续往下走。认证中间件不返回 401

关键机制:

机制说明
Handler 同请求缓存同一个 scheme 的 handler 只初始化一次,避免重复创建
handleAuthenticateOnce()同实例只执行一次认证逻辑,授权中间件再调时直接返回缓存结果
forwardAuthenticate可配置转发,当前方案不做认证,委托给目标方案
认证失败不拦截失败时什么都不写——不设 statusCode,不抛异常

深入执行细节

认证中间件的完整执行链路(handler 初始化、认证结果传递、与授权中间件的交互)已整合到本文档中。授权细节见 授权策略

四种认证方式

Spire 内置四种认证方式,从简单到完整认证中心逐级递进:

认证方式一句话描述典型场景
JWT BearerAuthorization: Bearer <token> 头读取并校验 JWTAPI、移动端、前后端分离
Cookie加密票据写入浏览器 Cookie,请求自动携带浏览器登录态、服务端页面
自定义Basic认证继承 AuthenticationHandler,自己写读取和校验逻辑Basic、HMAC、签名头、网关票据
Identity Server独立的 OIDC/OAuth2 认证中心,完整协议端点单点登录(SSO)、多客户端统一认证

JWT Bearer

请求头: Authorization: Bearer eyJhbGciOi...


        JwtBearerHandler
        1. 读取 Token
        2. 校验签名/issuer/audience/有效期
        3. 从 claims 构建 ClaimsPrincipal


        context.user = principal

适合无状态 API。服务端不存会话,每次请求靠 Token 自证身份。

POST /login → 验证凭据 → signIn() → Set-Cookie: .spire.auth=<加密票据>

GET /profile ──────────────────────────────┘(浏览器自动携带)


        CookieAuthenticationHandler
        1. 读取 Cookie
        2. 解密 + 反序列化票据
        3. 还原 ClaimsPrincipal


        context.user = principal

适合浏览器登录态。票据可存在 Cookie 里,也可只存会话键、真实票据放服务端。challenge() 默认跳登录页(302)而非返回 401

自定义Basic认证

三步接入自定义协议:

  1. 定义 Options(继承 AuthenticationSchemeOptions
  2. 继承 AuthenticationHandler<TOptions>,重写 handleAuthenticate()
  3. 通过 extend AuthenticationBuilder 暴露 addXxx() 注册入口
cangjie
// 核心:你只需要写这段
public func handleAuthenticate(): AuthenticateResult {
    let header = this.context.request.headers.authorization.first ?? String.empty
    // 1. 读凭证
    if (header.isEmpty() || !header.startsWith("Basic ")) {
        return AuthenticateResult.noResult()  // 没有可处理的凭证,跳过
    }
    // 2. 校验凭证
    let credential = parseBasic(header)
    if (credential.username == options.username &&
        credential.password == options.password) {
        // 3. 成功:创建票据
        let principal = ClaimsPrincipal([ClaimsIdentity(Some(this.scheme.name))])
        return AuthenticateResult.success(AuthenticationTicket(principal, this.scheme.name))
    }
    return AuthenticateResult.fail("Invalid credentials")
}

如何选择

需要独立认证中心(SSO / 多客户端)? → Identity Server

   否 ↓
   客户端是浏览器? → Cookie 认证

   否 ↓
   客户端是 API / 移动端? → JWT Bearer

   否 ↓
   内置方案不满足协议要求? → 自定义Basic认证

架构组件速查

认证体系涉及的核心类型:

组件角色
AuthenticationMiddleware请求管道的认证入口,驱动认证流程
IAuthenticationService统一入口:authenticate() / challenge() / forbid() / signIn() / signOut()
IAuthenticationSchemeProvider方案注册与查询,管理默认方案
IAuthenticationHandlerProvider从 DI 或缓存中获取 Handler 实例
AuthenticationScheme一个认证方案(name + handlerType)
AuthenticationHandler<TOptions>认证处理器的抽象基类——你实现 handleAuthenticate()
AuthenticationBuilder注册方案的构建器:addJwtBearer() / addCookie() / addScheme()
AuthenticateResult三种结果:success / fail / noResult
AuthenticationTicket认证产生的结果票据(ClaimsPrincipal + AuthenticationProperties)

详细 API 参考

组件的方法签名、初始化参数、转发配置等细节,参见 API 文档

认证 vs 授权

两者在 Spire 中严格解耦,但紧密配合:

维度认证 Authentication授权 Authorization
问题你是谁?你能做什么?
中间件useAuthentication()useAuthorization()
产物context.user放行 / 401 / 403
失败行为不拦截请求未认证 → challenge()401;已认证但权限不够 → forbid()403
校验对象Token / Cookie / 自定义凭证Endpoint.metadata 中的授权策略
核心组件AuthenticationHandlerAuthorizationHandler

关键认知:认证中间件不返回 401。 它只负责识别身份。401 由授权中间件发现"用户未认证 + 策略要求认证"时触发。

详细授权流程参见 授权策略

相关专题

按你的场景选择入口: