在OO PHP中使用来自不同对象的password_hash和password_verify

时间:2017-05-05 10:07:23

标签: php

我正在使用OO PHP编写MVC样式的应用程序,并且在尝试注册/登录用户时尝试使用不同的类时遇到了问题。本质上,我有一个抽象的User类,包含一些常见的属性和函数,以及2个扩展它的类:一个用户尝试登录时创建的LoginUser类和一个用户尝试注册时创建的RegisterUser类。

我的问题是:当我使用在RegisterUser类中调用的查询(使用密码上的password_hash函数)成功将用户添加到我的数据库时,然后尝试通过LoginUser类中调用的查询进行登录(使用password_verify函数)查询结果返回false,即使提供的密码肯定是注册时输入的密码。

我的问题是:password_verify函数是否必须由使用password_hash函数创建哈希的同一个类的对象调用?如果是这样,为什么?我试过看PHP文档,搜索结果也没有回复!

我之所以这样说是因为如果所有函数都保存在一个User类中,而不是继承的类,则注册/登录将会成功。

我的用户类:

abstract class User {

protected $checkedUserName = '';
protected $checkedPassword = '';

public function __construct($uncheckedUserName, $uncheckedPassword) {

  $this->checkedUserName = $this->validateAndSanitizeUserName($uncheckedUserName);
  $this->checkedPassword = $this->validateAndSanitizePassword($uncheckedPassword);
}

protected function validateAndSanitizeUserName($uncheckedUserName) {
  $string = filter_var($uncheckedUserName, FILTER_VALIDATE_EMAIL); // Checks input is an email
  $string = filter_var($string, FILTER_SANITIZE_EMAIL); // Removes illegal chars
  $string = filter_var($string, FILTER_SANITIZE_SPECIAL_CHARS); // Removes HTML tags, etc replacing them with char codes
  return $string;
}

protected function validateAndSanitizePassword($uncheckedPassword) {
  $string = filter_var($uncheckedPassword, FILTER_VALIDATE_REGEXP, ["options"=>["regexp"=>"/(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}/"]]); // Checks the password against the regex on the form
  $string = filter_var($string, FILTER_SANITIZE_SPECIAL_CHARS); // Removes HTML tags, etc replacing them with char codes
  return $string;
}

protected function checkIfUserExists() {
  // Set the initial status of user exists
  $userExists = false;
  // Open a connection to the database
  $con = Db::getInstance();

  // Prepare the query
  $checkIfUserExists = $con->prepare("SELECT * FROM users2 WHERE username=?");
  //Execute the query with the checked username
  $checkIfUserExists->execute([$this->checkedUserName]);
  // Set $userExists dependent on result
  if($checkIfUserExists->rowCount() !== 0) {
    $userExists = true;
  }
  return $userExists;
}

}

我的LoginUser类:

class LoginUser extends User{

public function __construct($uncheckedUserName, $uncheckedPassword) {

  parent::__construct($uncheckedUserName, $uncheckedPassword);
}

private function getPasswordHashes() {
  // Only connect to the database when connection is needed
  $con = Db::getInstance();

  // Check if username and password match
  // Prepare the query
  $checkUser = $con->prepare("SELECT * from users2 WHERE username = ?");
  // Execute the query using an array to bind the parameter to ?
  $checkUser->execute([$this->checkedUserName]);

  return $checkUser;
}

public function getLogInResult() {
  // Initialise the results variable
  $resultsFound = 0;

  // Only proceed if the username actually exists
  if($this->checkIfUserExists()) {

    // Call the function to get the records that match the username
    $checkUser = $this->getPasswordHashes();

    // Check to see if exactly one match was found and verify the password
    if($checkUser->rowCount() === 1) { // Note this may not work in other databases - it does in MySQL
      foreach($checkUser as $user) {
        if(password_verify($this->checkedPassword, $user['passwordHash'])) {
          $resultsFound++;
        }
      }
    }
  return $resultsFound;
}

} }

我的RegisterUser类:

lass RegisterUser extends User{

private $checkedFirstName = '';
private $checkedLastName = '';

public function __construct($uncheckedUserName, $uncheckedPassword, $uncheckedFirstName, $uncheckedLastName) {

  parent::__construct($uncheckedUserName, $uncheckedLastName);
  $this->checkedFirstName = $this->sanitizeString($uncheckedFirstName);
  $this->checkedLastName = $this->sanitizeString($uncheckedLastName);
}

private function sanitizeString($uncheckedString) {
  $string = filter_var($uncheckedString, FILTER_SANITIZE_STRING);

  return $string;
}

private function insertUserDetails() {
  // Hash the supplied password in preparation for insertion
  //$hashedPassword = password_hash($this->checkedPassword, PASSWORD_DEFAULT);

  // Connect to the database
  $con = Db::getInstance();

  // Prepare the query
  $addUser = $con->prepare("INSERT INTO users2 VALUES (?, ?, ?, ?)");

  // Execute the query using an array to bind the parameters
  $addUser->execute([$this->checkedUserName, password_hash($this->checkedPassword, PASSWORD_DEFAULT), $this->checkedFirstName, $this->checkedLastName]);

  // Return the result
  return $addUser;
}

public function getRegisterResult() {
  // Initialise the variable to store the result state
  $result = false;

  // Only proceed if the username does not exist
  if(!($this->checkIfUserExists())) {
    $addUser = $this->insertUserDetails();

    // If the details were successfully added
    if($addUser->rowCount() === 1) {
      $result = true;
    }
  }
  return $result;
}

}

因此,在填写注册表单时,会在新的getRegisterResult()对象上调用RegisterUser函数。登录时,会在新的getLoginResult()对象上调用LoginUser函数,但结果返回false ...

1 个答案:

答案 0 :(得分:0)

在回答我的问题时,哪些类使用password_hash和password_verify并不重要,如果与验证密码匹配并且数据库中的哈希值应该返回肯定结果!

问题出在__construct()类的RegisterUser - 对$uncheckedLastName而不是$uncheckedPassword传递的父级的调用,因此在注册时设置的密码是不是密码字段中提供的内容,而是LastName字段中提供的内容!