ThinkPHP5中实现密码重置接口的具体业务逻辑是怎样的?
- 内容介绍
- 文章标签
- 相关推荐
本文共计881个文字,预计阅读时间需要4分钟。
直接重置管理员密码,未经原密码验证,必须绕过登录态、独立入口,否则就不是重置而是修改——这是接口设计的第一条规则。
重置密码接口必须隔离登录态验证
ThinkPHP5 的 updatepwd 或 revisepassword 这类方法默认依赖 Session::get('uid') 或 Session::get('admin'),属于「已登录用户修改自身密码」逻辑,不能用于找回场景。重置接口必须完全跳过 session 检查,否则攻击者可伪造 session 绕过身份核验。
- 控制器方法顶部禁止调用
checkLogin()、isLogin()等中间件或手动校验 - 路由需单独定义,例如
route::post('api/password/reset', 'Index/resetPassword'),不挂载任何 auth 中间件 - 若使用命令行重置(如
tp_password_reset),入口是think命令而非 HTTP 请求,天然隔离 Web 层 session
数据库字段加密方式决定重置实现方式
直接写入数据库的值,必须匹配当前系统实际使用的哈希算法,否则重置后仍无法登录。MD5 是历史遗留坑,password_hash() 才是 TP5.1+ 推荐方式。
- 查表确认密码字段长度:若为 32 字符,极大概率是
md5($pass);若为 ~60 字符(含 $2y$ 开头),则是password_hash()生成的 Bcrypt - MD5 场景下可用硬编码:
Db::name('admin')->where('username', 'admin')->update(['password' => md5('123456')]) - Bcrypt 场景下必须用 PHP 函数生成:
password_hash('123456', PASSWORD_DEFAULT),不能手写哈希串 - 切勿在重置接口里复用旧密码校验逻辑(如
md5($data['password_origin']) == $db_pass),这会让重置变成伪重置
带验证码的重置流程需分三步原子化处理
短信/邮箱验证码不是装饰,是防止暴力刷重置的核心防线。TP5 中常见错误是把「发送」和「校验+更新」揉在一个接口里,导致状态不一致。
立即学习“PHP免费学习笔记(深入)”;
- 第一步:发送验证码接口(
sendResetCode)只做两件事——生成随机 6 位码、存入缓存(Cache::set('reset_code_'.$mobile, $code, 300))、调用云片/阿里云 SDK 发送 - 第二步:提交重置请求(
doResetPassword)先校验缓存中是否存在且未过期,再比对输入码,**校验通过后立即删除缓存**,防重放 - 第三步:更新密码前,必须再次查库确认该账号存在且未被禁用(
status = 1),避免重置已注销账户 - 所有步骤失败都应返回统一结构:
{'code'=>0, 'msg'=>'验证码错误'},不暴露「用户不存在」等敏感信息
最易被忽略的是缓存时效与删除时机——验证码缓存没删干净,攻击者可重复使用;缓存时间设太短,正常用户频繁触发限流;设太长,又增加撞库风险。300 秒(5 分钟)+ 单次有效 + 校验即删,是当前平衡点。
本文共计881个文字,预计阅读时间需要4分钟。
直接重置管理员密码,未经原密码验证,必须绕过登录态、独立入口,否则就不是重置而是修改——这是接口设计的第一条规则。
重置密码接口必须隔离登录态验证
ThinkPHP5 的 updatepwd 或 revisepassword 这类方法默认依赖 Session::get('uid') 或 Session::get('admin'),属于「已登录用户修改自身密码」逻辑,不能用于找回场景。重置接口必须完全跳过 session 检查,否则攻击者可伪造 session 绕过身份核验。
- 控制器方法顶部禁止调用
checkLogin()、isLogin()等中间件或手动校验 - 路由需单独定义,例如
route::post('api/password/reset', 'Index/resetPassword'),不挂载任何 auth 中间件 - 若使用命令行重置(如
tp_password_reset),入口是think命令而非 HTTP 请求,天然隔离 Web 层 session
数据库字段加密方式决定重置实现方式
直接写入数据库的值,必须匹配当前系统实际使用的哈希算法,否则重置后仍无法登录。MD5 是历史遗留坑,password_hash() 才是 TP5.1+ 推荐方式。
- 查表确认密码字段长度:若为 32 字符,极大概率是
md5($pass);若为 ~60 字符(含 $2y$ 开头),则是password_hash()生成的 Bcrypt - MD5 场景下可用硬编码:
Db::name('admin')->where('username', 'admin')->update(['password' => md5('123456')]) - Bcrypt 场景下必须用 PHP 函数生成:
password_hash('123456', PASSWORD_DEFAULT),不能手写哈希串 - 切勿在重置接口里复用旧密码校验逻辑(如
md5($data['password_origin']) == $db_pass),这会让重置变成伪重置
带验证码的重置流程需分三步原子化处理
短信/邮箱验证码不是装饰,是防止暴力刷重置的核心防线。TP5 中常见错误是把「发送」和「校验+更新」揉在一个接口里,导致状态不一致。
立即学习“PHP免费学习笔记(深入)”;
- 第一步:发送验证码接口(
sendResetCode)只做两件事——生成随机 6 位码、存入缓存(Cache::set('reset_code_'.$mobile, $code, 300))、调用云片/阿里云 SDK 发送 - 第二步:提交重置请求(
doResetPassword)先校验缓存中是否存在且未过期,再比对输入码,**校验通过后立即删除缓存**,防重放 - 第三步:更新密码前,必须再次查库确认该账号存在且未被禁用(
status = 1),避免重置已注销账户 - 所有步骤失败都应返回统一结构:
{'code'=>0, 'msg'=>'验证码错误'},不暴露「用户不存在」等敏感信息
最易被忽略的是缓存时效与删除时机——验证码缓存没删干净,攻击者可重复使用;缓存时间设太短,正常用户频繁触发限流;设太长,又增加撞库风险。300 秒(5 分钟)+ 单次有效 + 校验即删,是当前平衡点。

