简介
本技术文档聚焦 DouPHP 后台与前台的“记住我”(Remember-Me)令牌认证机制,系统性地说明令牌的生成、存储、验证、过期管理、自动续登以及与 Session 认证的协同工作。文档同时覆盖基于 Cookie 的持久化登录方案的安全要点(安全存储、跨域、浏览器兼容)、令牌刷新、撤销登录、多端登录控制、泄露防护、重放攻击防护、令牌轮换策略等安全最佳实践。
项目结构
围绕令牌认证的关键代码分布在以下位置:
- 后台管理员认证 Guard:admin/service/auth/AuthService.php
- 前台会员认证 Guard:front/facade/Auth.php
- 用户服务(含 Remember-Me 令牌签发与 Cookie 封装):_/module/user/core/service/user/UserAuthService.php
- 后台认证中间件:admin/middleware/AuthMiddleware.php
- CSRF 中间件与前端注入脚本:admin/middleware/CsrfMiddleware.php、admin/view/js/dou.csrf.js
- 认证管理器(多 Guard 注册与解析):core/foundation/auth/AuthManager.php
- 安全响应头基类:core/foundation/middleware/AbstractSecurityHeadersMiddleware.php
- 应用密钥常量定义:config/config.php
graph TB
A["请求进入"] --> B["AuthMiddleware<br/>恢复会话/续登"]
B --> C["AuthService<br/>校验/写入状态"]
C --> D["UserAuthService<br/>签发/清理 Remember-Me Cookie"]
B --> E["CsrfMiddleware<br/>表单CSRF校验"]
E --> F["业务控制器"]
D --> G["数据库 user/admin<br/>token/token_expires_at"]
B --> H["安全响应头<br/>HSTS/X-Frame-Options等"]
核心组件
- 后台管理员认证 Guard:负责凭据校验、会话写入、Remember-Me 令牌发放与续登恢复。
- 前台会员认证 Guard:负责凭据校验、会话写入、Remember-Me 令牌恢复与会话心跳。
- 用户服务:提供统一的 Cookie 设置/删除封装、Remember-Me 令牌签发、登录失败计数与锁定、IP 限流等能力。
- 认证管理器:统一注册与解析 admin/front/api 三端的 Guard,避免默认 Guard 带来的歧义。
- CSRF 中间件:后台/前台分别维护静态令牌与一次性令牌策略,配合前端脚本自动注入 X-CSRF-Token。
- 安全响应头中间件:在 HTTP 边界下发基线安全头,增强传输层安全。
架构总览
下图展示了后台管理员通过 Remember-Me 自动续登的完整流程,包括 Cookie 读取、哈希比对、会话重建与 CSRF 令牌补发。
sequenceDiagram
participant Browser as "浏览器"
participant AdminMW as "后台认证中间件"
participant AdminAuth as "后台AuthService"
participant DB as "数据库(admin)"
participant Csrf as "CSRF管理器"
Browser->>AdminMW : 携带 DOU_TOKEN Cookie 访问受保护页面
AdminMW->>AdminAuth : restoreFromSession(ip)
AdminAuth->>AdminAuth : tryRememberLogin(ip)
AdminAuth->>DB : 根据 token 哈希查询管理员记录
DB-->>AdminAuth : 返回管理员行(含 token_expires_at)
alt 令牌有效且未过期
AdminAuth->>AdminAuth : 写入 session(admin_id/shell/ontime)
AdminAuth->>Csrf : 生成 static_admin 令牌
AdminAuth-->>AdminMW : 返回管理员载荷
AdminMW-->>Browser : 继续处理请求
else 令牌无效或已过期
AdminAuth->>AdminAuth : 删除 DOU_TOKEN Cookie
AdminAuth-->>AdminMW : 返回 null
AdminMW-->>Browser : 重定向到登录页
end
详细组件分析
后台管理员 Remember-Me 令牌机制
- 令牌生成:登录成功时可选择发放 Remember-Me 令牌;随机字符串经 SHA-256 哈希后存入管理员表,并设置带过期时间的 Cookie。
- 令牌验证:每次请求先尝试从 Cookie 中读取令牌,计算哈希并与数据库匹配;若有效则重建会话并补发 CSRF 静态令牌,避免后续 POST 被拒。
- 过期管理:数据库保存过期时间,校验失败或过期时清除 Cookie 并清理相关字段。
- 自动续登:在会话缺失时优先尝试 Remember-Me 续登,成功后再走常规会话校验。
flowchart TD
Start(["开始"]) --> CheckCookie{"存在 DOU_TOKEN ?"}
CheckCookie --> |否| EndNo["结束(未续登)"]
CheckCookie --> |是| Hash["计算 token 哈希"]
Hash --> QueryDB["按哈希查询管理员记录"]
QueryDB --> Valid{"记录存在且未过期?"}
Valid --> |否| ClearCookie["删除 DOU_TOKEN Cookie"] --> EndNo
Valid --> |是| BuildSession["写入 session(admin_id/shell/ontime)"]
BuildSession --> GenCSRF["生成 static_admin 令牌"]
GenCSRF --> UpdateLast["更新最近登录信息"]
UpdateLast --> EndYes["结束(续登成功)"]
前台会员 Remember-Me 令牌机制
- 令牌生成:登录成功时可签发 Remember-Me 令牌;随机字符串经 SHA-256 哈希后存入用户表,并设置带过期时间的 Cookie。
- 令牌恢复:初始化阶段若会话缺失,尝试通过 Cookie 中的令牌恢复登录态;恢复成功后写入会话并标记为已登录。
- 会话校验:使用 shell(基于用户标识、密码与应用密钥的哈希)与数据库记录进行一致性校验,防止会话伪造。
- 心跳管理:定期刷新会话心跳,超时则清空会话。
sequenceDiagram
participant Browser as "浏览器"
participant FrontAuth as "前台Auth"
participant UserSvc as "UserAuthService"
participant DB as "数据库(user)"
Browser->>FrontAuth : 请求受保护页面
FrontAuth->>FrontAuth : resolveUserContext()
alt 会话存在
FrontAuth->>FrontAuth : verifySession(userId, shell)
FrontAuth-->>Browser : 继续处理
else 会话缺失
FrontAuth->>FrontAuth : restoreFromToken()
FrontAuth->>DB : 按 token 哈希查询用户
DB-->>FrontAuth : 返回用户(含过期时间)
alt 令牌有效且未过期
FrontAuth->>UserSvc : login(user, field, remember=true)
FrontAuth-->>Browser : 继续处理
else 令牌无效或过期
FrontAuth->>UserSvc : deleteCookie(DOU_TOKEN)
FrontAuth-->>Browser : 视为未登录
end
end
CSRF 与令牌协同
- 后台:登录后下发共享静态令牌 static_admin;CSRF 中间件自动校验,异常路由豁免由路由声明式配置。
- 前台:登录会员共享静态令牌 static_user;匿名表单使用一次性令牌抗重放。
- 前端:全局脚本自动为非幂等 AJAX 注入 X-CSRF-Token,兼容 jQuery 与 fetch。
classDiagram
class CsrfMiddleware {
+tokenIdFor(module, action, sub, candidates) string
+getTokenRoutes() array
+reject() void
}
class CsrfFacade {
+generate(id) string
+token(id) mixed
+verify(token, id) bool
+isOneTime(id) bool
}
class CsrfJS {
+injectXCSRFHeader()
}
CsrfMiddleware --> CsrfFacade : "使用"
CsrfJS --> CsrfFacade : "读取 meta 暴露的 token"
安全响应头与传输安全
- 基线安全头:X-Content-Type-Options、X-Frame-Options、Referrer-Policy、Permissions-Policy。
- HSTS:仅在 HTTPS 且配置开启时下发。
- 作用范围:仅对命中路由生效,404 自渲染不在覆盖范围内。
依赖关系分析
- 认证管理器集中管理多 Guard,确保调用点明确当前端(admin/front/api)。
- 后台认证中间件依赖 AuthService 进行会话恢复与续登。
- 前台认证依赖 UserAuthService 完成 Remember-Me 令牌签发与 Cookie 操作。
- CSRF 中间件与前端脚本共同保障表单与 AJAX 提交的安全性。
graph LR
AM["AuthManager"] --> AAS["Admin AuthService"]
AM --> FA["Front Auth"]
FA --> UAS["UserAuthService"]
AAM["Admin AuthMiddleware"] --> AAS
ACM["Admin CsrfMiddleware"] --> CS["Csrf Facade"]
JS["dou.csrf.js"] --> CS
性能考量
- 会话心跳:前台与后台均维护 ontime 字段,超时则清空会话,减少长期驻留风险。
- 懒加载:前台 Auth 构造期不实例化子服务,仅在需要时延迟创建,降低启动开销。
- 数据库查询:Remember-Me 续登仅在会话缺失时触发,避免冗余查询。
- CSRF 令牌:静态令牌复用,减少频繁生成与校验成本。
故障排查指南
- 续登失败:检查 DOU_TOKEN Cookie 是否存在、是否 HttpOnly/Secure、域名与路径是否正确;核对数据库中 token 与过期时间。
- CSRF 校验失败:确认模板是否输出 csrf-token meta;检查前端脚本是否注入 X-CSRF-Token;查看路由是否豁免了 CSRF。
- 会话过期:检查 ontime 是否被刷新;确认中间件管道是否执行;必要时重新登录以重建会话。
- 账号锁定/IP 限流:查看登录失败计数与锁定时间;确认 IP 是否在限流窗口内。
结论
DouPHP 的 Remember-Me 令牌认证采用“随机令牌 + 哈希存储 + 过期时间”的设计,结合会话校验与 CSRF 防护,实现了安全的持久化登录体验。后台与前台分别通过独立的 Guard 与中间件协作,确保续登、校验与安全性的一致性。建议在生产环境启用 HTTPS、合理配置 SameSite/HttpOnly/Secure,并结合审计日志与限流策略提升整体安全水位。
附录:实现示例与最佳实践
令牌刷新
- 目标:在用户活跃期间延长有效期,避免频繁重新登录。
- 建议做法:在关键敏感操作(如修改密码、支付)后重新签发 Remember-Me 令牌,并更新数据库过期时间。
- 参考路径:
- 后台签发与续登逻辑:admin/service/auth/AuthService.php:452-459、admin/service/auth/AuthService.php:376-403
- 前台签发与恢复逻辑:_'/module/user/core/service/user/UserAuthService.php:80-123、front/facade/Auth.php:371-400
撤销登录(登出)
- 目标:清除所有登录凭证,确保无法再续登。
- 建议做法:调用登出方法清理会话与 Cookie;后台登出会显式删除 DOU_TOKEN。
- 参考路径:
- 后台登出:admin/service/auth/AuthService.php:183-193
- 前台登出:front/facade/Auth.php:303-318
多端登录控制
- 目标:限制同一账号同时在线的设备数或强制单端登录。
- 建议做法:在登录时记录设备指纹(UA/设备ID),并在续登时校验;超过上限则撤销旧令牌或强制下线。
- 参考路径:
- 前台会话与令牌恢复:front/facade/Auth.php:470-510
- 用户服务 Cookie 封装:_'/module/user/core/service/user/UserAuthService.php:108-144
令牌泄露防护
- 建议措施:
- Cookie 设置 HttpOnly、Secure、SameSite=Lax,避免 XSS 窃取与跨站携带。
- 使用 HTTPS 传输,启用 HSTS。
- 定期轮换令牌,缩短有效期。
- 记录登录审计日志,发现异常立即撤销。
- 参考路径:
- Cookie 设置封装:_'/module/user/core/service/user/UserAuthService.php:108-144
- 安全响应头中间件:core/foundation/middleware/AbstractSecurityHeadersMiddleware.php:23-49
- 应用密钥常量:config/config.php:48-50
重放攻击防护
- 建议措施:
- 使用 CSRF 静态令牌与一次性令牌组合,表单提交必须携带正确令牌。
- 前端自动注入 X-CSRF-Token,后端中间件严格校验。
- 参考路径:
- 后台 CSRF 中间件:admin/middleware/CsrfMiddleware.php:24-54
- 前端 CSRF 注入脚本:admin/view/js/dou.csrf.js:13-60
令牌轮换策略
- 建议策略:
- 登录成功后轮换令牌,避免重用。
- 敏感操作后强制轮换。
- 过期后自动清理数据库与 Cookie。
- 参考路径:
- 后台轮换与续登:admin/service/auth/AuthService.php:165-193、admin/service/auth/AuthService.php:376-403
- 前台轮换与恢复:front/facade/Auth.php:371-400
与 Session 认证的协同
- 设计要点:
- 优先使用 Session 认证;当 Session 缺失时尝试 Remember-Me 续登。
- 续登成功后重建会话并刷新心跳,保证后续请求正常。
- 登出时同时清理 Session 与 Remember-Me Cookie。
- 参考路径:
- 后台续登与会话重建:admin/service/auth/AuthService.php:208-227
- 前台续登与会话校验:front/facade/Auth.php:470-510