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 子句。这没有意义,因为

  1. 您已经找到第一个查询的用户并验证了他们,并且

  2. 原始密码永远不会与数据库中的密码相匹配,因为数据库中的密码是经过哈希处理的(因为它应该是这样的,因此您在代码的前面使用了 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。安全最佳实践建议,当凭据无效时,不要让用户知道问题出在用户名还是密码(或两者)上。例如,如果您披露用户名错误,则表明恶意方可以丢弃该用户名并尝试另一个用户名,同样,如果您披露仅密码不正确,则表明他们应该继续尝试破解该用户名的密码。在任何一种情况下,您都应该简单地声明“凭据无效”,这不会提供有关如何缩小有效登录搜索范围的任何线索。