简介
本技术文档聚焦 DouPHP 的 API 认证中间件,围绕 ApiUserAuthMiddleware(即 api/middleware/UserAuthMiddleware)对 AbstractUserAuthMiddleware 的具体实现展开,系统说明以下要点:
- API 令牌验证流程:从请求头提取 Bearer Token,交由 API Guard 解析并注入用户上下文。
- 请求签名检查:当前代码库未内置通用请求签名校验;如需签名,应在网关或自定义中间件中实现。
- JSON 响应处理:统一通过 ApiResponse 输出标准包络,包含 code、message、data、errors、request_id。
- 认证方式:当前实现基于不透明随机 token(Bearer Token),由 ApiTokenService 签发与校验;App Key/Secret 与签名机制不在本仓库默认实现中。
- 权限控制:通过路由级鉴权策略表(auth_modes、work_required)限制接口访问范围与工作端身份。
- 未认证响应:返回标准 JSON 错误(401 未登录、403 无工作端身份)。
- 配置选项:在 api/init/middleware.php 中按模块/动作/子段声明 public/optional/required 及 work_required。
- 安全考虑:防重放、签名、速率限制等需结合网关或扩展中间件实现;当前提供 ThrottleMiddleware 作为限流基础。
- 与 Web 认证差异:API 使用无状态 token 鉴权,Web 使用会话型鉴权;RESTful 最佳实践强调幂等、无状态、标准化错误码。
- 移动端集成:小程序/APP 通过 Authorization: Bearer <token> 调用受保护接口,并在必要时刷新 token。
项目结构
API 认证相关的关键位置如下:
- 中间件实现:api/middleware/UserAuthMiddleware.php
- 基类骨架:core/foundation/middleware/AbstractUserAuthMiddleware.php
- 鉴权策略解析:core/foundation/middleware/UserAuthPolicy.php
- 鉴权配置:api/init/middleware.php
- API Guard:api/facade/Auth.php
- 统一响应与错误码:core/web/http/ApiResponse.php、core/foundation/api/ApiCodes.php
graph TB
A["客户端请求<br/>Authorization: Bearer <token>"] --> B["API 入口<br/>index.php"]
B --> C["中间件管道<br/>UserAuthMiddleware"]
C --> D["抽象基类<br/>AbstractUserAuthMiddleware.handle()"]
D --> E["鉴权策略<br/>UserAuthPolicy.resolve()"]
E --> F{"模式判定<br/>public/optional/required"}
F --> |public| G["直接放行"]
F --> |optional| H["尝试解析登录态"]
F --> |required| I["必须登录"]
H --> J["API Guard<br/>Auth::resolveUserContext(token)"]
I --> J
J --> K["注入上下文<br/>Auth::hydrate(context)"]
K --> L{"是否需要工作端身份<br/>work_required"}
L --> |是且缺失| M["拒绝:403 FORBIDDEN"]
L --> |否或满足| N["继续下游控制器"]
M --> O["统一JSON响应<br/>ApiResponse.error(...)"]
I --> P["拒绝:401 UNAUTHORIZED"]
P --> O
核心组件
- UserAuthMiddleware(API 端会员认证中间件)
- 职责:从请求头提取 Bearer Token,调用 API Guard 解析用户上下文,注入到 Auth 实例;根据策略决定是否放行或拒绝。
- 关键方法:configFile、resolveContext、inject、hasWorkIdentity、rejectUnauthenticated、rejectForbidden。
- AbstractUserAuthMiddleware(模板方法基类)
- 职责:封装公共鉴权流程(读取配置、策略决策、三态分支、注入身份、work 子策略判断)。
- 关键方法:setRouteParameters、handle、loadConfig。
- UserAuthPolicy(鉴权策略解析器)
- 职责:根据 module/action/sub/parent 与配置表生成 mode 与 workRequired 决策。
- 关键方法:resolve、buildCandidates。
- API Guard(api/facade/Auth)
- 职责:校验 token、构建用户上下文、注入身份缓存、查询工作端信息。
- 关键方法:checkLoginState、resolveUserContext、hydrate、workId。
- 统一响应与错误码
- ApiResponse:构造标准 JSON 响应(code/message/data/errors/request_id)。
- ApiCodes:定义业务码常量(如 UNAUTHORIZED、FORBIDDEN、RATE_LIMITED 等)。
架构总览
API 认证采用“中间件 + 策略 + Guard”的分层设计:
- 中间件负责 HTTP 边界输入(Bearer Token)与输出(JSON 错误)。
- 策略层将路由片段映射为鉴权模式与是否要求工作端身份。
- Guard 层负责 token 校验与用户上下文构建。
- 响应层统一封装错误与成功数据。
classDiagram
class AbstractUserAuthMiddleware {
+handle(next) mixed
+setRouteParameters(params) void
-loadConfig() array
#configFile() string
#resolveContext() array
#inject(context) void
#hasWorkIdentity() bool
#rejectUnauthenticated() void
#rejectForbidden() void
}
class UserAuthMiddleware {
+configFile() string
+resolveContext() array
+inject(context) void
+hasWorkIdentity() bool
+rejectUnauthenticated() void
+rejectForbidden() void
}
class UserAuthPolicy {
+resolve(module, action, sub, parent, authModes, workRequired) array
-buildCandidates(module, action, sub, parent) array
}
class Auth {
+id() int
+user() array
+check() bool
+guest() bool
+workId() int
+work() array
+hydrate(context) void
+reset() void
+checkLoginState(token) array
+resolveUserContext(token) array
}
class ApiResponse {
+payload(code, message, data, errors) array
+success(data, message) JsonResponse
+error(code, message, errors, httpStatus, data) JsonResponse
+throwSuccess(data, message) never
+throwError(code, message, errors, httpStatus, data) never
+requestId() string
}
class ApiCodes {
<<constants>>
OK
BUSINESS_RULE_VIOLATION
INVALID_PARAMS
VALIDATION_FAILED
UNAUTHORIZED
AUTH_REQUIRED
FORBIDDEN
NOT_FOUND
QUOTA_EXCEEDED
RATE_LIMITED
SERVER_ERROR
AI_CONFIG_UNAVAILABLE
CHAT_NOT_FOUND
CHAT_CREATE_FAILED
CHAT_SAVE_FAILED
}
UserAuthMiddleware --|> AbstractUserAuthMiddleware : "继承"
AbstractUserAuthMiddleware --> UserAuthPolicy : "策略决策"
UserAuthMiddleware --> Auth : "解析/注入上下文"
UserAuthMiddleware --> ApiResponse : "统一错误响应"
ApiResponse --> ApiCodes : "使用业务码常量"
详细组件分析
UserAuthMiddleware(API 端会员认证中间件)
- 配置文件路径:返回 API 端中间件配置路径,供基类加载 auth_modes 与 work_required。
- 上下文解析:从请求头获取 Bearer Token,调用 API Guard 的 resolveUserContext 解析用户上下文。
- 身份注入:将上下文注入到 API Guard,使后续控制器可通过 auth('api') 获取用户与工作端信息。
- 工作端身份判断:当策略要求 work 身份时,检查当前用户是否具备有效工作端 ID。
- 拒绝逻辑:
- 未认证:返回 401 UNAUTHORIZED 的标准 JSON 错误。
- 无工作端身份:返回 403 FORBIDDEN 的标准 JSON 错误。
sequenceDiagram
participant Client as "客户端"
participant MW as "UserAuthMiddleware"
participant Base as "AbstractUserAuthMiddleware"
participant Policy as "UserAuthPolicy"
participant Guard as "API Guard (Auth)"
participant Resp as "ApiResponse"
Client->>MW : "HTTP 请求含 Authorization : Bearer <token>"
MW->>Base : "handle(next)"
Base->>Policy : "resolve(module, action, sub, parent, authModes, workRequired)"
Policy-->>Base : "{mode, workRequired}"
alt mode == "public"
Base-->>Client : "直接放行"
else mode == "optional" or "required"
Base->>MW : "resolveContext()"
MW->>Guard : "resolveUserContext(token)"
Guard-->>MW : "{ok, userId, userProfile, work, workId}"
opt ok == false
alt mode == "required"
MW->>Resp : "error(UNAUTHORIZED, 401)"
Resp-->>Client : "JSON 401"
else mode == "optional"
Base-->>Client : "匿名放行"
end
else ok == true
MW->>Guard : "hydrate(context)"
alt workRequired && !hasWorkIdentity
MW->>Resp : "error(FORBIDDEN, 403)"
Resp-->>Client : "JSON 403"
else 满足条件
Base-->>Client : "放行至控制器"
end
end
end
API Guard(api/facade/Auth)
- 令牌校验:checkLoginState 接收 token,调用 ApiTokenService 解析用户 ID,并校验用户存在性。
- 上下文构建:resolveUserContext 组合用户资料与工作端信息,返回统一上下文结构。
- 身份注入:hydrate 将上下文写入 Guard 实例,暴露 id/user/workId/work 等方法供控制器使用。
- 工作端查询:内部通过 UserService 获取工作端详情,用于 work_required 策略判断。
flowchart TD
Start(["进入 resolveUserContext"]) --> CheckToken["校验 token<br/>checkLoginState(token)"]
CheckToken --> TokenOk{"校验通过?"}
TokenOk --> |否| EmptyCtx["返回空上下文<br/>{ok:false,...}"]
TokenOk --> |是| BuildProfile["构建用户资料<br/>buildUserProfile(userId)"]
BuildProfile --> FetchWork["获取工作端详情<br/>fetchWorkRow(userId)"]
FetchWork --> ReturnCtx["组装上下文<br/>{ok:true, userId, userProfile, work, workId}"]
EmptyCtx --> End(["结束"])
ReturnCtx --> End
鉴权策略(UserAuthPolicy)
- 候选键构建:按精确度从高到低生成 module/sub/action、module/sub、module/action、parent/sub/action、parent/sub、module、parent 等候选键。
- 模式决策:遍历候选键匹配 auth_modes 配置表,命中则确定 mode(public/optional/required)。
- 工作端策略:若候选键命中 work_required 列表,则标记 workRequired=true。
flowchart TD
S(["开始"]) --> Normalize["规范化 module/action/sub/parent"]
Normalize --> Candidates["构建候选键列表"]
Candidates --> MatchMode{"匹配 auth_modes"}
MatchMode --> ModeSet["设置 mode"]
ModeSet --> MatchWork{"命中 work_required?"}
MatchWork --> WorkFlag["设置 workRequired"]
WorkFlag --> Return["返回 {mode, workRequired}"]
Return --> E(["结束"])
配置项(api/init/middleware.php)
- auth_modes:按模块/动作/子段声明 public/optional/required,决定鉴权强度。
- work_required:命中后额外校验工作端身份,适用于后台管理或工作人员专属接口。
- 新增模块必须在 auth_modes 显式登记,避免新模块以匿名形态上线。
统一响应与错误码
- ApiResponse.payload/error/success:统一输出 code、message、data、errors、request_id。
- ApiCodes:定义业务码常量,如 UNAUTHORIZED、FORBIDDEN、RATE_LIMITED 等,客户端据此做分支处理。
依赖关系分析
- 中间件依赖:
- 抽象基类:AbstractUserAuthMiddleware 提供 handle 流程与配置加载。
- 策略解析:UserAuthPolicy 依据配置表生成鉴权决策。
- API Guard:Auth 负责 token 校验与上下文构建。
- 响应工具:ApiResponse 与 ApiCodes 提供统一错误格式与业务码。
- 外部依赖:
- ApiTokenService:由 API Guard 懒加载,负责 token 签发与解析(具体实现在服务层)。
- Request:通过 request()->bearerToken() 获取令牌。
- 语言包:lang_has/lang 用于国际化错误消息。
graph LR
MW["UserAuthMiddleware"] --> BASE["AbstractUserAuthMiddleware"]
BASE --> POLICY["UserAuthPolicy"]
MW --> GUARD["API Guard (Auth)"]
MW --> RESP["ApiResponse"]
RESP --> CODES["ApiCodes"]
GUARD --> TOKEN["ApiTokenService"]
MW --> REQ["Request (bearerToken)"]
性能与扩展性
- 中间件链优化:
- 策略解析仅进行字符串匹配与数组查找,时间复杂度低。
- Guard 上下文构建涉及数据库查询,应结合缓存减少重复 IO。
- 扩展点:
- 可在中间件前增加签名校验中间件(网关或应用层)。
- 可结合 ThrottleMiddleware 实现速率限制,防止滥用。
- 可扩展 ApiTokenService 支持 token 刷新与黑名单吊销。
故障排查指南
- 401 未登录:
- 检查请求头是否携带 Authorization: Bearer <token>。
- 确认 token 是否有效且未被吊销。
- 查看 ApiResponse.error(ApiCodes::UNAUTHORIZED, ...) 的输出结构。
- 403 无工作端身份:
- 检查策略是否要求 work 身份(work_required)。
- 确认用户是否具备工作端角色与权限。
- 查看 ApiResponse.error(ApiCodes::FORBIDDEN, ...) 的输出结构。
- 调试信息:
- 使用 ApiResponse.requestId() 追踪请求链路。
- 结合日志记录 token 解析失败原因(missing_credential、invalid_credential)。
结论
DouPHP 的 API 认证中间件通过“中间件 + 策略 + Guard”的分层架构,实现了清晰、可配置的鉴权流程。基于 Bearer Token 的无状态认证适合 RESTful API,配合统一的 JSON 响应与错误码,便于客户端处理。通过 auth_modes 与 work_required 的配置,可实现细粒度的接口访问控制。对于签名、防重放与速率限制等安全能力,建议在网关或扩展中间件中实现,并与现有中间件链协同工作。
附录:移动端集成与安全建议
移动端集成指南
- 小程序/APP 调用受保护接口时,需在请求头添加 Authorization: Bearer <token>。
- 首次登录成功后保存 token,后续请求自动附带。
- 遇到 401 时提示重新登录或刷新 token。
- 遇到 403 时提示无相应工作端权限。
安全加固建议
- 防重放攻击:
- 在网关或中间件层引入 nonce + timestamp 校验,确保请求唯一性与时效性。
- 请求签名验证:
- 服务端提供签名算法(如 HMAC-SHA256),客户端对请求体与参数排序后计算签名并附带。
- 服务端校验签名一致性,拒绝篡改请求。
- 速率限制:
- 结合 ThrottleMiddleware 或网关限流,按 IP/用户维度限制请求频率。
- 令牌有效期与刷新:
- 短生命周期 access_token + 长生命周期 refresh_token。
- 提供刷新接口,服务端校验 refresh_token 有效性并签发新 access_token。
- 最小权限原则:
- 通过 work_required 与策略表限制敏感接口访问范围。
- 控制器内进一步校验资源归属(如订单属主过滤)。