5

我是一名学生,这是我第一次编写软件。它是 LAMP 堆栈上的 Web 应用程序,作为该 Web 应用程序的一部分,我编写了以下函数来在创建新用户时与数据库交互:

public function CreateUser($username, $password, $email){
  global $DBHandler, $SQLStatement;
  $SQLStatement = $DBHandler->prepare("SELECT id FROM users WHERE username = :username AND verified > 0");
  $SQLStatement->bindParam(':username', $username);
  $SQLStatement->execute();
  $check = $SQLStatement->fetch();
  if ($check['id']){
    return 2;
  }else{
    $SQLStatement = $DBHandler->prepare("SELECT id FROM users WHERE email = :email AND verified > 0");
    $SQLStatement->bindParam(':email', $email);
    $SQLStatement->execute();
    $check = $SQLStatement->fetch();
    if ($check['id']){
      return 3;
    }else{
      /* Edited out code that generates a random verification code, a random salt, and hashes the password. */
      $SQLStatement = $DBHandler->prepare("INSERT INTO users (username, email, passwordhash, salt, verifycode) VALUES (:username, :email, :passwordhash, :salt, :verifycode)");
      $SQLStatement->bindParam(':username', $username);
      $SQLStatement->bindParam(':email', $email);
      $SQLStatement->bindParam(':passwordhash', $passwordhash);
      $SQLStatement->bindParam(':salt', $salt);
      $SQLStatement->bindParam(':verifycode', $verifycode);
      $SQLStatement->execute();
      return 1;
    }
  }
}

$DBHandler 在其他地方作为 PHP 数据对象启动。

它遵循以下基本步骤:

  1. 查询数据库以查看是否有人已经验证了具有所需用户名的帐户。
  2. 如果用户名可用,请执行另一个查询并对电子邮件进行相同的检查。
  3. 如果用户名和电子邮件都可用,则生成验证码、加盐、散列密码、执行第三次查询以将所有内容写入数据库,然后返回 1(表示成功)。

这个想法是,在您的帐户得到验证(已验证 = '1')之前,不会保留用户名和电子邮件。如果您单击通过电子邮件发送的验证链接,则在其他地方有一个脚本会将您的已验证列更改为“1”。

我的第一个问题是这样的:

人 A 提出了用户名“Lorem”,而人 B 有一个未经验证的帐户,该帐户也提出了用户名“Lorem”。是否可以按以下顺序执行查询?

  1. 人员 A 的脚本确定用户名“Lorem”未由经过验证的用户使用
  2. 人 B 的脚本使用用户名“Lorem”验证他们的帐户
  3. 人员 A 的脚本使用用户名“Lorem”创建其未经验证的帐户

我的第二个问题是:

是否可以将此脚本中的 3 个查询组合成 1 个查询,使用 SQL 而不是 PHP 表示的相同 if/else 逻辑,如果是这样,这会提高性能和/或防止上述情况发生吗?

4

1 回答 1

2

对于更并发的解决方案,您可以设置insert条件:

insert  into users 
        (username, email, ...)
select  :username, :email, ...
where   not exists
        (
        select  *
        from    users
        where   verified > 0
                and (username = :username
                     or email = :email)
        )

这在 MySQL 中仍然不是 100% 安全的,但在实践中应该足够了。

请注意,用户创建中的并发问题是一个奢侈问题。我只是设置了一个和你一样的基本版本,并专注于你网站更有趣的方面!

于 2012-09-16T07:25:05.340 回答