简介
本文件面向第三方开发者,系统化说明 DouPHP API 的认证与授权机制。重点覆盖:
- API 端基于不透明随机 token 的鉴权流程(签发、校验、吊销)
- 后台管理员基于会话的登录态恢复与中间件保护
- 多 Guard 体系与统一身份读取契约
- 请求头格式、错误响应、安全最佳实践与常见问题排查
项目结构
DouPHP 将认证能力按“端”拆分:
- API 端:无状态 token 鉴权,通过中间件从请求头提取 Bearer token,解析用户上下文并注入到当前请求
- 后台端:基于 Session 的管理员登录态恢复与路由级中间件保护
- 基础层:Guard 契约与 AuthManager 统一管理不同端的 Guard 实例
graph TB
subgraph "API 端"
AMW["UserAuthMiddleware<br/>从请求头取 Bearer token"]
AFA["Api Facade<br/>校验 token / 构建上下文"]
ATS["ApiTokenService<br/>签发/解析/吊销 token"]
end
subgraph "后台端"
ADMW["Admin AuthMiddleware<br/>恢复 Session 登录态"]
ASV["Admin AuthService<br/>attempt/login/logout"]
end
subgraph "基础层"
AMGR["AuthManager<br/>注册/解析 Guard"]
GC["GuardContract<br/>统一身份读取契约"]
end
AMW --> AFA --> ATS
ADMW --> ASV
AFA -.-> AMGR
ASV -.-> AMGR
AMGR --> GC
核心组件
- API 认证中间件:负责从 HTTP 边界抽取 Authorization: Bearer <token>,调用 API Guard 解析用户上下文并注入;未认证返回 401,无工作端身份返回 403
- API Guard:实现 GuardContract,提供 id()/user()/check()/guest() 等统一接口;对外暴露 checkLoginState() 与 resolveUserContext() 用于令牌校验与上下文构建
- API Token 服务:负责不透明随机 token 的签发、解析、吊销;库内仅保存 sha256(token),支持过期时间与活跃心跳刷新
- 后台认证中间件:通过 Session 恢复管理员登录态,未登录重定向至登录页
- 后台认证服务:实现 StatefulGuardContract,提供 attempt/login/logout、remember-me、IP 限流、账号锁定等
- AuthManager:集中管理 admin/front/api 三端 Guard 工厂与实例缓存,强制显式 guard 名
- GuardContract:定义跨端统一的身份读取契约,确保 id()/user()/check()/guest() 语义一致
架构总览
下图展示一次受保护的 API 请求从进入中间件到控制器可用的完整链路,以及后台管理员登录态恢复路径。
sequenceDiagram
participant C as "客户端"
participant MW as "API 认证中间件"
participant G as "API Guard"
participant T as "ApiTokenService"
participant DB as "数据库"
participant CTRL as "业务控制器"
C->>MW : "HTTP 请求<br/>Authorization : Bearer <token>"
MW->>G : "resolveUserContext(token)"
G->>T : "resolve(token)"
T->>DB : "按 token_hash 查询并校验过期"
DB-->>T : "命中则返回 user_id"
T-->>G : "user_id"
G->>DB : "读取用户资料与工作端信息"
DB-->>G : "用户资料 + 工作端行"
G-->>MW : "上下文 {ok, userId, userProfile, work, workId}"
MW->>G : "hydrate(上下文)"
MW-->>CTRL : "放行请求已注入身份"
CTRL-->>C : "业务响应"
详细组件分析
API 令牌认证流程(签发、验证、吊销)
- 签发:由登录流程调用 ApiTokenService::issue(),生成 64 位十六进制随机 token,写入 user_token 表(仅存 token_hash),设置过期时间(默认 30 天),返回明文 token 给客户端
- 验证:中间件从请求头提取 Bearer token,调用 API Guard 的 resolveUserContext(),内部委托 ApiTokenService::resolve() 进行哈希匹配、过期检查与活跃心跳更新
- 吊销:登出或敏感操作时调用 revoke() 删除当前设备 token;全局登出或改密场景调用 revokeAllForUser() 清空该用户所有 token
flowchart TD
Start(["开始"]) --> Issue["签发 token<br/>生成随机串 + 写入 user_token"]
Issue --> Store["仅存储 token_hash<br/>记录过期时间"]
Store --> Return["返回明文 token 给客户端"]
Return --> Use["客户端在后续请求携带<br/>Authorization: Bearer <token>"]
Use --> Verify{"验证 token"}
Verify --> |有效| Hydrate["构建用户上下文并注入"]
Verify --> |无效/过期| Reject["拒绝访问401/403"]
Hydrate --> End(["结束"])
Reject --> End
后台管理员认证与会话管理
- 登录尝试:AuthService::attempt() 校验凭据、IP 限流、账号锁定、密码校验,成功后写入 Session 并可选发放 remember-me 凭证
- 会话恢复:AuthMiddleware 调用 auth('admin')->restoreFromSession(),优先尝试 remember-me 自动续登,再校验 Session 中的 admin_id 与 shell
- 登出:清除 Session、清理 remember cookie、重置实例状态
sequenceDiagram
participant Admin as "管理员"
participant AMW as "Admin 认证中间件"
participant AS as "Admin AuthService"
participant S as "Session/Cookie"
Admin->>AMW : "访问受保护页面"
AMW->>AS : "restoreFromSession(ip)"
AS->>S : "读取 remember cookie / Session"
S-->>AS : "凭据或会话数据"
AS-->>AMW : "成功返回 payload 或 null"
alt 未登录
AMW-->>Admin : "重定向到登录页"
else 已登录
AMW-->>Admin : "放行请求"
end
权限验证机制(角色、资源、操作)
- 角色与操作权限:后台侧通过菜单与权限判定模块(如 AdminGate)结合管理员 action_list 控制;API 侧通过工作端身份(workId/workRow)区分不同角色与资源范围
- 资源与操作:控制器或服务层依据当前守卫提供的 id()/user()/work() 信息进行数据隔离与操作限制;具体策略由各业务模块实现
- 决策点:API 中间件在解析上下文后,可依据 workId 判断是否具备工作端身份,否则返回 403
类与职责关系图
classDiagram
class GuardContract {
+id() int
+user() array
+check() bool
+guest() bool
}
class AuthManager {
+extend(name, factory)
+guard(name) object
+has(name) bool
+forget(name) void
}
class ApiAuthFacade {
+id() int
+user() array
+check() bool
+guest() bool
+workId() int
+work() array
+checkLoginState(token) array
+resolveUserContext(token) array
+hydrate(context) void
+reset() void
}
class ApiTokenService {
+issue(userId, ip) string
+resolve(token) int
+revoke(token) void
+revokeAllForUser(userId) void
}
class AdminAuthService {
+attempt(credentials, remember, ip) bool
+login(user, remember, ip) void
+logout() void
+restoreFromSession(ip) array|null
+hydrate(admin) void
+reset() void
}
class UserAuthMiddleware {
+configFile() string
+resolveContext() array
+inject(context) void
+hasWorkIdentity() bool
+rejectUnauthenticated() void
+rejectForbidden() void
}
class AdminAuthMiddleware {
+handle(next) mixed
}
ApiAuthFacade --> ApiTokenService : "使用"
UserAuthMiddleware --> ApiAuthFacade : "调用"
AdminAuthMiddleware --> AdminAuthService : "调用"
AuthManager --> ApiAuthFacade : "解析 api guard"
AuthManager --> AdminAuthService : "解析 admin guard"
GuardContract <|.. ApiAuthFacade
GuardContract <|.. AdminAuthService
依赖关系分析
- API 中间件依赖 API Guard 与 ApiTokenService;Guard 依赖数据库与服务层以构建用户上下文
- 后台中间件依赖后台认证服务;认证服务依赖 Session/Cookie 与数据库
- AuthManager 作为统一入口,避免隐式默认 guard,强制显式指定端类型,降低耦合与误用风险
graph LR
MW_API["API 认证中间件"] --> G_API["API Guard"]
G_API --> T_API["ApiTokenService"]
T_API --> DB["数据库"]
MW_ADMIN["Admin 认证中间件"] --> G_ADMIN["Admin AuthService"]
G_ADMIN --> SESS["Session/Cookie"]
G_ADMIN --> DB
AMGR["AuthManager"] --> G_API
AMGR --> G_ADMIN
性能与安全考量
- 性能
- API token 解析采用哈希定位与恒定时间比较,减少时序攻击面;命中后按 60 秒节流更新 last_active,避免高频写盘
- 签发时清理过期 token 行,防止表膨胀
- 后台 remember-me 自动续登仅在必要时触发,且补发 CSRF 静态令牌,避免后续表单校验失败导致假性登出
- 安全
- API token 为高熵随机串,库内仅存 sha256(token),泄库不可逆推有效凭证
- 后台 Session 使用 shell 校验,配合 remember cookie 与超时机制,降低会话劫持风险
- 建议全站启用 HTTPS,防止 token 与 Session 在传输中被窃听
- 建议对关键接口增加速率限制与验证码防护,防止暴力破解与重放攻击
故障排查指南
- 401 未登录
- 检查请求头是否包含 Authorization: Bearer <token>
- 确认 token 格式为 64 位十六进制字符串,且未过期
- 查看 ApiTokenService::resolve() 是否命中数据库记录
- 403 无工作端身份
- 检查 API Guard 解析出的 workId 是否为 0
- 确认用户是否绑定工作端身份
- 后台无法登录或频繁被重定向
- 检查 IP 限流与账号锁定状态
- 确认 remember cookie 是否有效且未过期
- 核对 Session 中 admin_id 与 shell 是否匹配
结论
DouPHP 的认证授权体系通过“端隔离 + 统一契约 + 中间件管道”的方式,实现了清晰的职责边界与可扩展的鉴权模型。API 端采用无状态不透明 token,后台端采用有状态 Session,两者均提供完善的生命周期管理与安全防护。第三方开发者可据此快速集成认证流程,并在业务层按需扩展权限策略。
附录:第三方集成与调用示例
- 请求头格式
- 认证请求需携带:Authorization: Bearer <token>
- 示例:Authorization: Bearer a1b2c3d4e5f6...(64 位十六进制)
- 令牌传递方式
- 小程序/移动端在每次请求时将 token 放入上述请求头
- 服务端中间件自动解析并注入用户上下文,无需业务代码重复处理
- 错误响应处理
- 未认证:返回 401,消息键通常为 login_timeout
- 无工作端身份:返回 403,消息键通常为 work_no_permission
- 其他业务错误:由控制器返回标准 JSON 响应
- 安全最佳实践
- 全站启用 HTTPS,避免明文传输 token
- 定期轮换密钥与配置,最小化泄露影响
- 对登录与敏感接口实施速率限制与验证码防护
- 登出时调用吊销接口,确保 token 立即失效
- 避免在日志与前端存储中明文记录 token