password_verify在if语句中失效排查及替代写法合理性咨询
嘿,这个问题确实有点让人摸不着头脑,我来帮你拆解一下可能的原因和解决方案:
首先,先看你代码里的一个小问题:用户名在数据库里应该是唯一的吧?那你用while循环其实是多余的——fetch_assoc()只会取出一条匹配的记录,循环一次就结束了,而且如果真的出现重复用户名的极端情况,第一个验证失败就直接跳转退出,会忽略后面的用户,逻辑上不太严谨。
接下来回到核心问题:为什么password_verify直接写在if里会失效,赋值给变量就正常?我觉得最可能的原因和跳转逻辑有关,而不是password_verify本身的问题:
看你原代码的else块:
else { header("LOCATION: index.php?error=Wrong password."); exit("wrong password<br>"); }
这里的exit("wrong password<br>")会直接输出内容到页面,而HTTP协议要求header必须在任何输出(包括空格、换行、文本)之前发送。一旦有输出,header跳转就会失效,页面会停留在当前页,看起来像是验证失败,但实际上是跳转没生效。
而当你把验证结果赋值给变量后,大概率是修改了else块的代码,去掉了exit里的输出内容,只保留了header和exit,这时候跳转正常生效,你就觉得验证功能恢复了。
另外,你提到重新加载页面后功能又正常,这可能是偶发的POST数据传递问题,或者数据库连接临时波动导致$row['password']的值异常,重新加载后数据恢复正常,验证也就成功了。
关于“把验证结果赋值给变量是否是不良实践”:完全不是!这种方式反而更推荐——不仅让代码逻辑更清晰,还方便调试(比如可以临时加var_dump($success)查看验证结果),对代码可读性和维护性都有好处。
最后给你优化后的代码参考,修正了循环逻辑和跳转问题:
$query = $db->query("SELECT * FROM users WHERE `username` = '" . $db->real_escape_string($_POST['username']) . "';"); if (!$query or $query == NULL) { echo $db->error . "<br>"; exit("EXIT: database error"); } // 直接取出一条记录,不需要循环 $row = $query->fetch_assoc(); if ($row) { $isPasswordValid = password_verify($_POST['password'], $row['password']); if ($isPasswordValid) { $db->close(); header("Location: main.php"); exit; } else { header("Location: index.php?error=Wrong password."); exit; // 不要在这里输出任何内容 } } else { // 处理用户名不存在的情况 header("Location: index.php?error=Username not found."); exit; }
内容的提问来源于stack exchange,提问作者kennyrkun

