登录总失败是因为密码被重复哈希:setpasswordattr和beforewrite中都执行了md5(),导致存入md5(md5('123456'));正确做法是仅在setpasswordattr中加密并加判空及是否已哈希的判断,避免二次处理。

TP6 模型事件里加密密码,为什么登录总失败?
因为加密逻辑被重复执行了两次:一次在 setPasswordAttr 修改器,另一次又在 beforeWrite 事件里调用了 md5()。数据库里存的是 md5(md5('123456')),而验证时只比对一层哈希,必然不匹配。
实操建议:
- 只在一个地方做字段级加密:优先用模型的
setPasswordAttr方法,它天然接收原始输入值,且只在赋值和保存前触发一次 - 别在
beforeWrite里再碰$data['password']——这个事件适合做日志记录、关联更新等非字段逻辑 - 加一层判断防二次加密:
public function setPasswordAttr($value) { if (strlen($value) === 32 && ctype_xdigit($value)) { return $value; } return md5($value); } - 调试时用
trace()打日志,别依赖dump(),TP6 默认日志路径是runtime/log/
想用自动填充($filter)加密,但 TP6 不生效?
TP6 已废弃 $auto/$filter 填充机制,create() 方法也不再存在。硬写 protected $filter = ['password' => 'md5'] 不会触发,save() 时直接忽略。
实操建议:
- 改用修改器(mutator):定义
setPasswordAttr($value),这是 TP6 官方推荐方式 - 如果必须兼容旧逻辑,可手动在控制器中处理:
$data = ['username' => 'admin', 'password' => '123456']; $data['password'] = md5($data['password']); $model->data($data)->save();
- 注意:TP6 的
validate()->save()不会自动应用$filter,只校验,不填充
加密后字段怎么查?where('password', $input) 为什么查不到?
因为你在拿明文去比密文。加密字段不能用于等值查询,更不能参与 like、order、group 等操作——AES 加密后字符串完全无序,且不可索引。
实操建议:
- 需要检索的字段(如身份证前6位、手机号前3位),单独建明文索引字段,加
index,和加密字段一起维护 - 绝对不要把加密逻辑下推到 SQL 层(比如用 MySQL 的
AES_ENCRYPT),否则密钥脱离应用控制,审计和轮换都成问题 - 如果业务强依赖模糊查询,得引入「可搜索加密」库(如
defuse/php-encryption),TP 自身不提供
用 Crypt::encrypt() 加密数据库字段,字段类型怎么设?
加密后内容是二进制,base64 编码后长度约等于原长度 × 1.33 + 16 字节(IV)。直接存 varchar(32) 肯定溢出,MySQL 会截断,解密失败。
实操建议:
- 字段类型必须设为
text或足够长的varchar(例如身份证号加密后建议varchar(255)) - 加密前先
base64_encode(),解密后反向base64_decode(),避免二进制污染字段 - 使用
think\facade\Crypt时,密钥由app.crypt_key配置项控制,别硬编码 - 示例修改器写法:
protected function setIdCardAttr($value) { return $value ? \think\facade\Crypt::encrypt(base64_encode($value)) : null; } protected function getIdCardAttr($value) { return $value ? base64_decode(\think\facade\Crypt::decrypt($value)) : null; }
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











