PHP PDO & MySQL * WHERE OR AND 语句问题
PHP PDO & MySQL * WHERE OR AND statement issue
我目前正在尝试找出为什么我的登录语句适用于使用用户名,而不适用于电子邮件(反之亦然,如果在查询中切换)
查询似乎只接受查询的第一个值,即使我把它放在括号中,它似乎也没有注册第二个参数。
运行 phpMyAdmin 中的查询完美运行,但在脚本中中断
乐于接受建议
$sql = "SELECT * FROM db_cms_users WHERE username = ? OR email = ? AND password = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID, $password])){
$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0){
$stmt = null;
header("location: login.php?error=nouser");
exit();
}
我试过了
$sql = "SELECT * FROM db_cms_users WHERE (username = ? OR email = ?) AND password = ?";
rowCount returns 如果我输入用户名,则为 true,但如果我输入电子邮件,则为 false。语句匹配 SQL 数据库。
转储变量
SQL: [76] SELECT * FROM db_cms_users WHERE username = ? OR email = ? AND password = ?
Sent SQL: [137] SELECT * FROM db_cms_users WHERE username = 'test@email.com' OR email = 'test@email.com' AND password = 'password'
Params: 3
- Key: Position #0: paramno=0 name=[0] "" is_param=1 param_type=2
- Key: Position #1: paramno=1 name=[0] "" is_param=1 param_type=2
- Key: Position #2: paramno=2 name=[0] "" is_param=1 param_type=2
Database output
Array
(
[0] => Array
(
[id] => 1
[0] => 1
[username] => test
[1] => test
[password] => y$QNKXEo3pnGPCjUMnfXlV..JJ4OFcSQJ5EVg75xOjlE7p5pL7Dqwau
[2] => y$QNKXEo3pnGPCjUMnfXlV..JJ4OFcSQJ5EVg75xOjlE7p5pL7Dqwau
[email] => test@email.com
[3] => test@email.com
[status] => 1
[4] => 1
[is_admin] => 1
[5] => 1
[registration] => 2021-11-13 12:21:28
[6] => 2021-11-13 12:21:28
)
)
文件:user.class.php
protected function loginUser($userID, $password){
$sql = "SELECT password FROM db_cms_users WHERE username = ? OR email = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID])){
$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0){
$stmt = null;
header("location: login.php?error=loginerror");
exit();
}
$hashedPwd = $stmt->fetchAll();
$checkPwd = password_verify($password, $hashedPwd[0]['password']);
if($checkPwd == false){
$stmt = null;
header("location: index.php?error=wrongpwd");
exit();
}elseif($checkPwd == true){
$sql = "SELECT * FROM db_cms_users WHERE username = ? OR email = ? AND password = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID, $password])){
#$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0){
$stmt = null;
header("location: login.php?error=nouser");
exit();
}
$row = $stmt->fetchAll();
//make session later
//nov 13/21
session_start();
$_SESSION['username'] = $row[0]['username'];
$_SESSION['uid'] = $row[0]['id'];
return true;
}
}
文件userContr.class.php
public function login($userID, $password){
$result = $this->loginUser($userID, $password);
return $result;
}
文件test.php
<?php
ob_start();
session_start();
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
include "includes/autoloader.inc.php";
$userObj = new UserView();
$data = $userObj->showUser(1);
echo "<pre>";
print_r ($data[0]);
echo "</pre>";
$userObj = new UserContr();
if(isset($_SESSION['uid'])){
echo "<h1>Welcome back ". $_SESSION['username'] ."!";
echo "<a href='?a=logout'>Logout</a>";
if(isset($_GET['a'])){
$a = $_GET['a'];
if($a == "logout"){
$userObj->logoutUser();
exit();
}
}
}else{
if(isset($_POST['loginUser'])){
$userID = $_POST['userid'];
$password = $_POST['password'];
$result = $userObj->login($userID, $password);
if($result == true){
header("location: index.php");
exit();
}else{
echo "There was a login error";
exit();
}
}else{
echo "<h1>Login</h1>";
echo "<form method='post' action>
<input type='text' name='userid' placeholder='Username/Email'>
<input type='password' name='password' placeholder='password'><br>
<input type='submit' name='loginUser' value='Login'>
</form>";
}
}
ob_end_flush();
SQL: 转储文件
-- Table structure for table `db_cms_users`
--
CREATE TABLE `db_cms_users` (
`id` int(11) NOT NULL,
`username` text NOT NULL,
`password` text NOT NULL,
`email` text NOT NULL,
`status` int(11) NOT NULL DEFAULT '0',
`is_admin` int(11) NOT NULL DEFAULT '0',
`registration` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `db_cms_users`
--
INSERT INTO `db_cms_users` (`id`, `username`, `password`, `email`, `status`, `is_admin`, `registration`) VALUES
(1, 'test', 'y$QNKXEo3pnGPCjUMnfXlV..JJ4OFcSQJ5EVg75xOjlE7p5pL7Dqwau', 'test@email.com', 1, 1, '2021-11-13 20:21:28');
感谢您的重大更新。上下文非常有用。看来您尝试做的事情不符合逻辑或没有必要。
首先,一切顺利。在您的 loginUser()
函数中,您正在根据电子邮件/用户名正确获取用户详细信息。然后您将使用 password_verify()
以正确的方式验证密码。这很好,很明智。
但是您似乎遇到麻烦的那一点没有多大意义。看起来在您验证密码后,您会进行 另一个 查询以获取 same 用户 - 除了这次您正在尝试将密码放入 WHERE
子句。这没有意义,因为
您已经找到第一个查询的用户并验证了他们,并且
原始密码永远不会与数据库中的密码相匹配,因为数据库中的密码是经过哈希处理的(因为它应该是这样的,因此您在代码的前面使用了 password_verify()
)。
你真的不需要第二个 SELECT 查询 - 你想用它实现什么?
如果您只是将第一个查询更改为 select 更多字段,那么您的问题就解决了 - 您可以将这些详细信息直接放入会话中,而无需 运行 另一个查询:
protected function loginUser($userID, $password) {
$sql = "SELECT username, id, password FROM db_cms_users WHERE username = ? OR email = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID])) {
$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0) {
$stmt = null;
header("location: login.php?error=loginerror");
exit();
}
$user = $stmt->fetchAll();
$checkPwd = password_verify($password, $user[0]['password']);
if($checkPwd == false) {
header("location: index.php?error=wrongpwd");
exit();
}
elseif($checkPwd == true) {
session_start();
$_SESSION['username'] = $user[0]['username'];
$_SESSION['uid'] = $user[0]['id'];
return true;
}
}
P.S。安全最佳实践建议,当凭据无效时,不要让用户知道问题出在用户名还是密码(或两者)上。例如,如果您披露用户名错误,则表明恶意方可以丢弃该用户名并尝试另一个用户名,同样,如果您披露仅密码不正确,则表明他们应该继续尝试破解该用户名的密码。在任何一种情况下,您都应该简单地声明“凭据无效”,这不会提供有关如何缩小有效登录搜索范围的任何线索。
我目前正在尝试找出为什么我的登录语句适用于使用用户名,而不适用于电子邮件(反之亦然,如果在查询中切换)
查询似乎只接受查询的第一个值,即使我把它放在括号中,它似乎也没有注册第二个参数。
运行 phpMyAdmin 中的查询完美运行,但在脚本中中断
乐于接受建议
$sql = "SELECT * FROM db_cms_users WHERE username = ? OR email = ? AND password = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID, $password])){
$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0){
$stmt = null;
header("location: login.php?error=nouser");
exit();
}
我试过了
$sql = "SELECT * FROM db_cms_users WHERE (username = ? OR email = ?) AND password = ?";
rowCount returns 如果我输入用户名,则为 true,但如果我输入电子邮件,则为 false。语句匹配 SQL 数据库。
转储变量
SQL: [76] SELECT * FROM db_cms_users WHERE username = ? OR email = ? AND password = ?
Sent SQL: [137] SELECT * FROM db_cms_users WHERE username = 'test@email.com' OR email = 'test@email.com' AND password = 'password'
Params: 3
- Key: Position #0: paramno=0 name=[0] "" is_param=1 param_type=2
- Key: Position #1: paramno=1 name=[0] "" is_param=1 param_type=2
- Key: Position #2: paramno=2 name=[0] "" is_param=1 param_type=2
Database output
Array
(
[0] => Array
(
[id] => 1
[0] => 1
[username] => test
[1] => test
[password] => y$QNKXEo3pnGPCjUMnfXlV..JJ4OFcSQJ5EVg75xOjlE7p5pL7Dqwau
[2] => y$QNKXEo3pnGPCjUMnfXlV..JJ4OFcSQJ5EVg75xOjlE7p5pL7Dqwau
[email] => test@email.com
[3] => test@email.com
[status] => 1
[4] => 1
[is_admin] => 1
[5] => 1
[registration] => 2021-11-13 12:21:28
[6] => 2021-11-13 12:21:28
)
)
文件:user.class.php
protected function loginUser($userID, $password){
$sql = "SELECT password FROM db_cms_users WHERE username = ? OR email = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID])){
$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0){
$stmt = null;
header("location: login.php?error=loginerror");
exit();
}
$hashedPwd = $stmt->fetchAll();
$checkPwd = password_verify($password, $hashedPwd[0]['password']);
if($checkPwd == false){
$stmt = null;
header("location: index.php?error=wrongpwd");
exit();
}elseif($checkPwd == true){
$sql = "SELECT * FROM db_cms_users WHERE username = ? OR email = ? AND password = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID, $password])){
#$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0){
$stmt = null;
header("location: login.php?error=nouser");
exit();
}
$row = $stmt->fetchAll();
//make session later
//nov 13/21
session_start();
$_SESSION['username'] = $row[0]['username'];
$_SESSION['uid'] = $row[0]['id'];
return true;
}
}
文件userContr.class.php
public function login($userID, $password){
$result = $this->loginUser($userID, $password);
return $result;
}
文件test.php
<?php
ob_start();
session_start();
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
include "includes/autoloader.inc.php";
$userObj = new UserView();
$data = $userObj->showUser(1);
echo "<pre>";
print_r ($data[0]);
echo "</pre>";
$userObj = new UserContr();
if(isset($_SESSION['uid'])){
echo "<h1>Welcome back ". $_SESSION['username'] ."!";
echo "<a href='?a=logout'>Logout</a>";
if(isset($_GET['a'])){
$a = $_GET['a'];
if($a == "logout"){
$userObj->logoutUser();
exit();
}
}
}else{
if(isset($_POST['loginUser'])){
$userID = $_POST['userid'];
$password = $_POST['password'];
$result = $userObj->login($userID, $password);
if($result == true){
header("location: index.php");
exit();
}else{
echo "There was a login error";
exit();
}
}else{
echo "<h1>Login</h1>";
echo "<form method='post' action>
<input type='text' name='userid' placeholder='Username/Email'>
<input type='password' name='password' placeholder='password'><br>
<input type='submit' name='loginUser' value='Login'>
</form>";
}
}
ob_end_flush();
SQL: 转储文件
-- Table structure for table `db_cms_users`
--
CREATE TABLE `db_cms_users` (
`id` int(11) NOT NULL,
`username` text NOT NULL,
`password` text NOT NULL,
`email` text NOT NULL,
`status` int(11) NOT NULL DEFAULT '0',
`is_admin` int(11) NOT NULL DEFAULT '0',
`registration` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `db_cms_users`
--
INSERT INTO `db_cms_users` (`id`, `username`, `password`, `email`, `status`, `is_admin`, `registration`) VALUES
(1, 'test', 'y$QNKXEo3pnGPCjUMnfXlV..JJ4OFcSQJ5EVg75xOjlE7p5pL7Dqwau', 'test@email.com', 1, 1, '2021-11-13 20:21:28');
感谢您的重大更新。上下文非常有用。看来您尝试做的事情不符合逻辑或没有必要。
首先,一切顺利。在您的 loginUser()
函数中,您正在根据电子邮件/用户名正确获取用户详细信息。然后您将使用 password_verify()
以正确的方式验证密码。这很好,很明智。
但是您似乎遇到麻烦的那一点没有多大意义。看起来在您验证密码后,您会进行 另一个 查询以获取 same 用户 - 除了这次您正在尝试将密码放入 WHERE
子句。这没有意义,因为
您已经找到第一个查询的用户并验证了他们,并且
原始密码永远不会与数据库中的密码相匹配,因为数据库中的密码是经过哈希处理的(因为它应该是这样的,因此您在代码的前面使用了
password_verify()
)。
你真的不需要第二个 SELECT 查询 - 你想用它实现什么?
如果您只是将第一个查询更改为 select 更多字段,那么您的问题就解决了 - 您可以将这些详细信息直接放入会话中,而无需 运行 另一个查询:
protected function loginUser($userID, $password) {
$sql = "SELECT username, id, password FROM db_cms_users WHERE username = ? OR email = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID])) {
$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0) {
$stmt = null;
header("location: login.php?error=loginerror");
exit();
}
$user = $stmt->fetchAll();
$checkPwd = password_verify($password, $user[0]['password']);
if($checkPwd == false) {
header("location: index.php?error=wrongpwd");
exit();
}
elseif($checkPwd == true) {
session_start();
$_SESSION['username'] = $user[0]['username'];
$_SESSION['uid'] = $user[0]['id'];
return true;
}
}
P.S。安全最佳实践建议,当凭据无效时,不要让用户知道问题出在用户名还是密码(或两者)上。例如,如果您披露用户名错误,则表明恶意方可以丢弃该用户名并尝试另一个用户名,同样,如果您披露仅密码不正确,则表明他们应该继续尝试破解该用户名的密码。在任何一种情况下,您都应该简单地声明“凭据无效”,这不会提供有关如何缩小有效登录搜索范围的任何线索。