我的注册码允许同一电子邮件注册两次(仅当表格连续多次提交时非常快)[重复]
Posted
技术标签:
【中文标题】我的注册码允许同一电子邮件注册两次(仅当表格连续多次提交时非常快)[重复]【英文标题】:My registration code is allowing the same email to register twice (only when the form is submitted multiple times in a row very fast) [duplicate] 【发布时间】:2017-06-09 05:41:06 【问题描述】:我用 html、javascript 和 php 编写了一些注册代码。 注册过程(上级)如下:
用户: 1. 在 HTML 表单中输入他们的电子邮件、姓名和密码(两次)。 2.他们按“创建帐户” 3. 然后在我的用户表中搜索他们的电子邮件以查看是否 存在。 4. 如果存在,则告知他们已经注册并提供了登录屏幕的链接。 5. 如果电子邮件不存在,则将其(连同其他用户详细信息)插入到我数据库中的用户表中
我的代码可以很好地实现这一点,但是在测试它时我发现了一个错误。如果用户非常快速地多次按下“创建帐户”,它允许相同的电子邮件地址注册两次。我能做些什么来阻止这种情况吗?
这是我的代码: jQuery/Javascript
$("#registration_form").on("submit", function(e)
//this is called when the form is submitted i.e when "create account" is pressed
e.preventDefault();
var registration_email = $('#registration_email').val();
var registration_password = $('#registration_password').val();
var registration_password_confirmation = $('#confirm_registration_password').val();
var registration_display_name = $('#registration_display_name').val();
//validate fields and check if passwords match.
//all values have passed validation testing therefore make ajax request
var params = 'registration_email' : registration_email, 'registration_password' : registration_password , 'registration_display_name' : registration_display_name, 'app_root_url':app_root_url;
$.ajax(
url: app_root_url + 'login_registration/registration_processing.php',
data: JSON.stringify(params),
type: "POST",
dataType: "json",
contentType: "application/json;charset=utf-8",
success: function(data)
var result = data;
var emailAlreadyExists = result.email_already_exists;
if(emailAlreadyExists)
//email already exists in our database and therefore the user is already registered so should use the login form
displayError('registration_feedback_message', 'This email already exists in the system. If you already have an account please <a href="#page-login">login here!</a>');
else
//email does not already exist in our database
//end success
);//end ajax
);
registration_processing.php(该文件的主要部分)
include_once '../includes/app_functions.php';
$app_root_url = filter_var($json->app_root_url, FILTER_SANITIZE_URL);
$email = filter_var($json->registration_email, FILTER_SANITIZE_EMAIL);
$password = filter_var($json->registration_password, FILTER_SANITIZE_STRING);
$display_name = filter_var($json->registration_display_name, FILTER_SANITIZE_STRING);
//more data filtering is performed here
$userPrivilegeID = 1; //basic user privilege
$userHasPassword = 1; //boolean 1 or 0
$profileImage = "images/profile_images/blank-avatar.png";
$results = registerUser($password, $email, $isAvatarImage, $profileImage, $userPrivilegeID, $display_name, $userHasPassword, $pdoConnection);
//create an array to store all values that we want to send back to the client side.
$data = array();
if($results['exception_occurred'] == true)
$data['exception_occurred'] = true;
$data['exception_message'] = $results['exception_message'];
echo json_encode($data);
else
$data['exception_occurred'] = false;
if($results['email_already_exists'] == true)
//email already exists. user is already registered and therefore has a password
//need to show error to user to say they are already registered and should use the login form.
$data['email_already_exists'] = true;
echo json_encode($data);
else
//email didnt exist so they have been registered
$data['email_already_exists'] = false;
//create an array which we will encrypt as our JWT token
$token = array();
$token['userID'] = $results['user_id'];
$token['email'] = $email;
$data['userID'] = $results['user_id'];
$data['user_is_subscriber'] = true;
$data['valid_user'] = true;
$data['userDetails'] = getProfile($results['user_id'], $pdoConnection);
$data['usertoken'] = JWT::encode($token, 'secret_server_key');
//echo data back to ajax request on client side
echo json_encode($data);
registerUser 函数(在 app_functions.php 中)
function registerUser($password, $email, $isAvatarImage, $profileImage, $userPrivilegeID, $display_name, $userHasPassword, $pdoConnection)
$data = array();
try
$data['exception_occurred'] = false;
//first check if that email already exists just in case
$query = "SELECT COUNT(userID) FROM users WHERE emailAddress=:emailAddress";
$statement = $pdoConnection->prepare($query);
$statement->bindValue(':emailAddress', $email, PDO::PARAM_STR);
$statement->execute();
$rowCount = $statement->fetchColumn(0);
if($rowCount > 0)
//email already exists. user is already registered and therefore has a password
//need to show error to user to say they are already registered and should use the login form.
$data['email_already_exists'] = true;
return $data;
else
$data['email_already_exists'] = false;
$hashedPassword = password_hash($password, PASSWORD_DEFAULT, ['cost' => 12]);
$query = "INSERT INTO users (emailAddress, password, isAvatarImage, profileImage, userPrivilegeID, displayName, userHasPassword) VALUES (:emailAddress, :password, :isAvatarImage, :profileImage, :userPrivilegeID, :displayName, :userHasPassword)";
$statement = $pdoConnection->prepare($query);
$statement->bindValue(':emailAddress', $email, PDO::PARAM_STR);
$statement->bindValue(':password', $hashedPassword, PDO::PARAM_STR);
$statement->bindValue(':isAvatarImage', $isAvatarImage, PDO::PARAM_INT);
$statement->bindValue(':profileImage', $profileImage, PDO::PARAM_STR);
$statement->bindValue(':userPrivilegeID', $userPrivilegeID, PDO::PARAM_INT);
$statement->bindValue(':displayName', $display_name, PDO::PARAM_STR);
$statement->bindValue(':userHasPassword', $userHasPassword, PDO::PARAM_INT);
$statement->execute();
$data['user_id'] = $pdoConnection->lastInsertId();
return $data;
catch(PDOException $e)
//throw new pdoDbException($e);
$data['exception_occurred'] = true;
$data['exception_message'] = $e->getMessage();
return $data;
我能想到的一个解决方案是在“创建帐户”按钮上放置一个计时器,这样多个 ajax 请求就不会如此接近?
编辑
在阅读了您的一些解决方案后,我正在考虑使电子邮件字段独一无二。谢谢 我在 phpMyAdmin 中工作,所以只需按“唯一”(在图像中突出显示)就这么简单吗?
编辑 2
我尝试将电子邮件创建为唯一键,但出现以下错误:
编辑 3
为了解决上述错误(在编辑 2 中),我将电子邮件地址字段的排序规则从 utf8mb4_unicode_ci 更改为 utf8_unicode_ci,然后我再次尝试按“unique”,它成功了。谢谢大家
【问题讨论】:
防止重复的正确方法是在数据库中使用唯一的约束/索引,而不是在应用程序中竞争查询。 显然是的,就是这么简单,但是如果你害怕的话,先复制一张桌子,然后在另一张桌子上做之前在副本上测试。 【参考方案1】:您可以将 UNIQUE CONSTRAINT 添加到您的电子邮件字段到数据库中。这样,如果您尝试插入已存在的电子邮件,则会失败。
【讨论】:
我有 userID 作为我的主键。我可以在不更改主键的情况下将 UNIQUE CONSTRAINT 添加到电子邮件字段吗?还是我也需要重新考虑我的主键?谢谢 您不必更改主键,但请确保您确实需要 userID。 好的,非常感谢。你能看到我的编辑吗?那么我只需要在phpMyAdmin的表格中选择“唯一”(在上图中突出显示)吗? 是的,你只需要这个。 太棒了!感谢您的帮助。【参考方案2】:使数据库中的电子邮件字段独一无二。我不推荐 PK,因为用户可能想更改他们的电子邮件地址。
【讨论】:
【参考方案3】:我始终建议在服务器和客户端上都进行验证。
在这种情况下,正如大家提到的,在服务器(数据库)中,您应该在电子邮件字段中定义一个unique constraint,这样重复的电子邮件就不会真正发生。
我还建议disable 在第一次单击“创建帐户”按钮后。您甚至可以将按钮的名称更改为“...请稍候”以指示正在执行操作。当您从服务器获得响应(成功或失败)时,您可以再次启用它。这将首先避免对服务器的任何不必要的连续调用。
【讨论】:
感谢您的建议。我会像你说的那样禁用按钮。【参考方案4】:您在 else 情况下返回了数据,而不是在 if 情况下现在有两种选择。 而不是在 else 块中返回数据,而是在 else 块之外返回它或 在 if 情况下也返回数据。
这会导致当它找到电子邮件 ID 但不返回时,如果您检查,它将为假,因此它允许注册用户为两个不同的帐户使用相同的电子邮件 ID
【讨论】:
感谢您指出这一点。我现在在 registerUser 函数中看到它。以上是关于我的注册码允许同一电子邮件注册两次(仅当表格连续多次提交时非常快)[重复]的主要内容,如果未能解决你的问题,请参考以下文章