在我的网站中,我有用户以电子邮件作为用户名注册
如果我试图手动添加一个新用户,而我使用的用户名(即邮件)已经存在,WP会投诉并告诉我该用户名已经注册
如果通过下面的代码以编程方式执行此操作,我会处理此问题并阻止注册
然而,我猜有一个用户多次单击表单提交按钮,成功地在我的WP用户数据库中复制了完全相同的用户
问题是,这怎么可能?
我该如何防止这种情况?
从评论中,换言之,重新表述问题:If I want to replicate this, how can I do this?
到目前为止,唯一有意义的解释是,我只能通过重新创建一个种族条件(frontend、postman、curl等等)来复制它
我不确定是否有办法阻止它(我想这就像防止DOS攻击一样)
我使用的代码是:
public function register_user($request = null)
{
$response = array();
$parameters = $request->get_json_params();
$email = sanitize_text_field($parameters[\'email\']);
$password = sanitize_text_field($parameters[\'password\']);
$domain = $_SERVER[\'HTTP_REFERER\'];
$uri = $_SERVER[\'REQUEST_URI\'];
$role = \'subscriber\';
$error = new WP_Error();
if (empty($email)) {
$error->add(...);
return $error;
}
if (!is_email($email)) {
$error->add(...);
return $error;
}
if (empty($password)) {
$error->add(...);
return $error;
}
$user_id = username_exists($email);
if (!$user_id && email_exists($email) == false) {
$user_id = wp_create_user($email, $password, $email);
if (!is_wp_error($user_id)) {
$user = get_user_by(\'id\', $user_id);
$user->set_role($role);
$response[\'code\'] = 200;
$response[\'message\'] = __("User \'" . $user_id . "\' ok ", "...");
} else {
return $user_id;
}
} else if ($user_id) {
$error->add(...);
return $error;
}
return new WP_REST_Response($response, 200);
}
我只是不明白这是怎么发生的
从注释中编辑,包括前端代码
<button type="text" value="" onClick={register}>
<div>...</div>
</button>
const register = async e => {
e.stopPropagation()
if (registering === \'working\') return
setRegistering(\'working\')
let id = input_id.current.value.trim()
let pass = input_pass.current.value
let pass2 = input_pass2.current.value
const success = pass === pass2 ? await auth.signup(id, pass, feedback) : \'not_equal_passwords\'
setRegistering(success)
if (success === \'yes\') navigate(\'/welcome\')
}
const signup = async (email, password) => {
const register = `${domain}${website}/wp-json/.../register`
const request = get_request(\'POST\')
const expression = /^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/
let success = password ? (email ? \'\' : \'no_id\') : \'no_pass\'
success = expression.test(String(email).toLowerCase()) ? \'\' : \'no_mail\'
if (!success) {
request.body = JSON.stringify({ email: email, password: password })
const response = await get_response(register, request, 5, 4000, \'json\')
success = response.code === \'user_exist\' ? \'user_exist\' : \'yes\'
}
return success
}
const get_response = async (url, request, times, time = 2000, json) => {
const rq = request || get_request(\'GET\')
let response = await Promise.race([fetch(url, rq), wait(time)])
let counter = 0
for (const _ of [...Array(times)]) {
if (counter >= times || response !== \'timed\') break
response = await Promise.race([fetch(url, request), wait(time)])
counter++
}
if (response === \'timed\') return
return json ? await response.json() : response
}
最合适的回答,由SO网友:Tom J Nowell 整理而成
是的,WordPress会在内部检查重复的电子邮件,但不会检查重复的用户名
为了测试这一点,我通过wp shell
:
wp_create_user( \'test\', \'password\', \'[email protected]\' );
第二次尝试的结果是:
=> class WP_Error#1962 (2) {
public $errors =>
array(1) {
\'existing_user_email\' =>
array(1) {
[0] =>
string(42) "Sorry, that email address is already used!"
}
}
public $error_data =>
array(0) {
}
}
当我尝试用相同的用户名注册不同的电子邮件时:
wp> wp_create_user( \'test\', \'password\', \'[email protected]\' );
=> class WP_Error#1981 (2) {
public $errors =>
array(1) {
\'existing_user_login\' =>
array(1) {
[0] =>
string(36) "Sorry, that username already exists!"
}
}
public $error_data =>
array(0) {
}
}
我还检查了代码,在
wp_insert_user
:
https://github.com/WordPress/WordPress/blob/f93ee2ca76164bcae721e4730c92ee1455fa1dd9/wp-includes/user.php#L1645-L1650
/*
* If there is no update, just check for `email_exists`. If there is an update,
* check if current email and new email are the same, or not, and check `email_exists`
* accordingly.
*/
if ( ( ! $update || ( ! empty( $old_user_data ) && 0 !== strcasecmp( $user_email, $old_user_data->user_email ) ) )
&& ! defined( \'WP_IMPORTING\' )
&& email_exists( $user_email )
) {
return new WP_Error( \'existing_user_email\', __( \'Sorry, that email address is already used!\' ) );
}
那么是什么原因造成的呢
目前没有足够的信息来诊断这一问题,而且你的用户名是电子邮件也无济于事,因为这样很容易忽视事情,或者把它们当作总是一样的。
目前我最好的猜测是竞争条件,如果使用负载平衡,这种情况更有可能发生。
因此,即使它没有创建多个用户,您仍然需要处理用户双击或三次单击注册按钮,否则他们会得到一个错误,即电子邮件已经存在(因为他们得到的是第二个请求的结果,而第一个请求使用的是电子邮件)
检查用户查看代码的更可靠方法:
$user_id = username_exists($email);
if (!$user_id && email_exists($email) == false) {
$user_id = wp_create_user($email, $password, $email);
} else if ($user_id) {
... \'ERROR in Registration, user email already exists \' ...
return $error;
}
wp_create_user
已执行这些检查,并返回用户id或错误对象。在这种情况下:
如果用户创建失败,您的代码将永远无法发现这一点,因为没有对结果进行检查,因此,让我们简化它:
$user_id = wp_create_user( $email, $password, $email );
if ( is_wp_error( $user_id ) ) {
// it failed!
....
return $error;
}
请记住一些其他事项:
- 不要将电子邮件用作用户名,自动生成它们,并在显示用户名时使用过滤器显示电子邮件
- 清理!!!因为您只显示了一个受约束的代码片段,所以可能涉及到清理或缺少它,但由于您不会共享周围的代码,因此无法判断。可能是您的电子邮件后面有空格和其他字符,您不需要将电子邮件作为用户名来使用。WordPress将在登录时接受用户名和电子邮件,如果您登录,则无需知道用户名如果您知道电子邮件,则无需检查电子邮件是否存在,
wp_insert_user
如果您的注册表单通过PHP表单提交而不是AJAX请求来运行,那么您的注册表单可能会更可靠,尽管您不能使其原子化,但您可以尝试减少步骤和并行请求的数量,在第一次单击时禁用注册按钮5秒钟,或者将其替换为进度微调器wp_create_user
呼叫wp_insert_user
, 所以使用后者。WP有很多“中间人”的功能,试图提供帮助,但通常他们出于向后兼容的原因,有一些微妙的行为,这些行为更令人讨厌,而不是帮助。去掉这些中间环节可以简化事情,减少调试步骤,在注册端点上放置一个临时值,如果没有,那么您可以通过快速bash脚本和Curl,每分钟创建数千个用户