You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PDO预编译语句函数修正及用户登录验证失败问题排查

解决PDO改写后登录验证失败的问题及代码修正

我帮你梳理下问题所在,以及对应的修复方案:

一、登录失败的核心问题

你在Auth类的getMemberByUsername方法里犯了两个关键错误:

  1. 方法没有返回查询结果:你只是把查询结果赋值给了类的$this->data属性,但调用方法时$user = $auth->getMemberByUsername($username);,这里$user会是null,因为方法没有return语句。
  2. 结果取值方式不匹配:你的DBController已经设置了默认的PDO::FETCH_OBJ(返回对象),但登录代码里却用数组下标$user[0]["member_password"]去取值,这会直接报错,导致password_verify永远不会成功,最终触发"Invalid Login"。

二、DBController里的冗余方法问题

你的insert和update方法完全是无效的:参数写的是$query,但内部用的是未定义的$sql,也没接收$args参数,逻辑完全混乱。其实你已经写了run方法,它可以处理所有类型的SQL语句(查询、插入、更新、删除),完全不需要单独的insert/update方法,直接删掉这两个方法即可,或者修正它们复用run方法。

三、修正后的代码

1. 修复Auth类的getMemberByUsername方法

class Auth {
    protected $db;
    protected $data;
    public function __construct() {
        $this->db = DBController::instance();
    }

    // 修正后:返回查询结果,且如果用户名唯一,用fetch()更高效
    function getMemberByUsername($username) {
        return $this->db->run("Select * from `members` where `member_name` = ?", [$username])->fetch();
    }

    // 其他方法优化:查询单条用fetch,批量用fetchAll,更新/插入无需fetchAll
    function getTokenByUsername($username,$expired) {
        return $this->db->run("Select * from `tbl_token_auth` where `username` = ? and `is_expired` = ?", [$username, $expired])->fetchAll();
    }

    function markAsExpired($tokenId) {
        $expired = 1;
        return $this->db->run("UPDATE `tbl_token_auth` SET `is_expired` = ? WHERE id = ?", [$expired, $tokenId]);
    }

    function insertToken($username, $random_password_hash, $random_selector_hash, $expiry_date) {
        return $this->db->run("INSERT INTO `tbl_token_auth` (username, password_hash, selector_hash, expiry_date) values (?, ?, ?,?)", [$username, $random_password_hash, $random_selector_hash, $expiry_date]);
    }
}

2. 修复登录验证代码

if (! empty($_POST["login"])) {
    $isAuthenticated = false;
    $username = $_POST["member_name"];
    $password = $_POST["member_password"];
    
    // 现在$user是单个对象(如果查到),或者false(没查到)
    $user = $auth->getMemberByUsername($username);
    
    // 先判断用户是否存在,再验证密码
    if ($user && password_verify($password, $user->member_password)) {
        $isAuthenticated = true;
    }

    // 后续处理登录成功/失败的逻辑...
}

3. 修复DBController类

删掉冗余的insert和update方法,同时增加连接异常捕获方便调试:

<?php
define('DB_HOST', 'xxx');
define('DB_NAME', 'xxx');
define('DB_USER', 'xxx');
define('DB_PASS', 'xxx');
define('DB_CHAR', 'xxx');
class DBController {
    protected static $instance;
    protected $pdo;
    public function __construct() {
        $opt = array(
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
            PDO::ATTR_EMULATE_PREPARES => FALSE,
        );
        $dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHAR;
        // 增加连接异常捕获,方便调试
        try {
            $this->pdo = new PDO($dsn, DB_USER, DB_PASS, $opt);
        } catch(PDOException $e) {
            die("数据库连接失败: " . $e->getMessage());
        }
    }
    public static function instance() {
        if (self::$instance === null) {
            self::$instance = new self;
        }
        return self::$instance;
    }
    public function __call($method, $args) {
        return call_user_func_array(array($this->pdo, $method), $args);
    }
    public function run($sql, $args = []) {
        if (!$args) {
            return $this->query($sql);
        }
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($args);
        return $stmt;
    }
}

四、额外建议

  • 确保数据库中members表的member_password字段是用password_hash()生成的哈希值,而不是明文或者其他加密方式,否则password_verify会失效。
  • 对于只查询单条数据的场景(比如根据用户名查用户),用fetch()代替fetchAll(),性能更好,代码也更简洁。

内容的提问来源于stack exchange,提问作者Maria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:49:27