Перенаправление пользователя с помощью хука 'wp_login_failed' при ошибках 'empty_username' или 'empty_password'
Предыстория
Я пытаюсь создать удобный фронтенд-логин для блога, оставляя при этом доступ к wp-login.php для пользователей, которые знают, как туда попасть. wp-login.php также необходим для аутентификации фронтенд-логинов, поэтому его нельзя полностью удалить.
Я почти добился цели, но у меня есть проблема с ошибками входа, особенно с пустым именем пользователя.
Вопрос
Кто-нибудь знает способ предотвратить отображение wp-login.php при ошибках пустого имени пользователя/пароля, избегая при этом отображения ошибки по той же причине, когда пользователь переходит прямо на wp-login.php?
Моя работа
Я нашел хук wp_login_failed и создал функцию ниже. По сути, она добавляет query-аргументы, указывающие на неудачную попытку входа и введенное имя пользователя, перед перенаправлением пользователя обратно на фронтенд-логин. С помощью этих аргументов я могу определить причину неудачи и показать сообщение пользователю.
add_action('wp_login_failed', 'djg_front_end_login_fail');
function djg_front_end_login_fail($username){
$referrer = (isset($_SERVER['HTTP_REFERER'])) ? $_SERVER['HTTP_REFERER'] : $_SERVER['PHP_SELF'];
$referrer = add_query_arg('result', 'failed', $referrer);
$referrer = add_query_arg('username', $username, $referrer);
if(!empty($referrer) && !strstr($referrer, 'wp-login') && !strstr($referrer, 'wp-admin')) :
wp_redirect($referrer);
exit;
endif;
}
Однако я заметил, что если поле имени пользователя было пустым, перенаправление не происходило, и пользователю все равно показывался wp-login.php.
При дальнейшем исследовании я обнаружил функцию wp_authenticate(), где заметил, что если первой ошибкой входа были empty_username или empty_password, хук wp_login_failed не вызывался. Чтобы решить эту проблему, я использовал фильтр authenticate и изменил код этих ошибок, добавив к ним префикс djg_, чтобы они не игнорировались.
add_filter('authenticate', 'djg_authenticate_login', 99, 3);
function djg_authenticate_login($user, $username, $password){;
if(is_wp_error($user)) :
$codes = $user->get_error_codes();
$messages = $user->get_error_messages();
$user = new WP_Error;
for($i = 0; $i <= count($codes) - 1; $i++) :
$code = $codes[$i];
if(in_array($code, array('empty_username', 'empty_password'))) :
$code = 'djg_' . $code;
endif;
$user->add($code, $messages[$i]);
endfor;
endif;
return $user;
}
Это работает, но теперь, если пользователь переходит прямо на страницу wp-login.php, которая должна оставаться доступной (как описано в Предыстории), отображается ошибка:
ОШИБКА: Поле имени пользователя пусто.
ОШИБКА: Поле пароля пусто.
Наконец, после долгих мучений я нашел в функции wp_signon() код, вызывающий эту проблему, но, на мой взгляд, там нет фильтров, позволяющих изменить поведение. По сути, он говорит, что если ошибки точно empty_username и empty_password, их нужно игнорировать:
if ( is_wp_error($user)) {
if ( $user->get_error_codes() == array('empty_username', 'empty_password')) {
$user = new WP_Error('', '');
}
return $user;
}
Детали
wp_authenticate()находится в/wp-includes/pluggable.php- Фильтр
authenticateнаходится внутри функцииwp_authenticate() - Хук
wp_login_failedнаходится внутри функцииwp_authenticate() wp_signon()находится в/wp-includes/user.php
Вопрос (снова)
Кто-нибудь знает способ предотвратить отображение wp-login.php при ошибках пустого имени пользователя, избегая при этом отображения ошибки по той же причине, когда пользователь переходит прямо на wp-login.php?
WordPress обрабатывает неудачные попытки входа двумя способами:
- Если введены неверные учетные данные, и оба поля - имя пользователя и пароль - содержат
значения, то это событие может быть перехвачено хуком
wp_login_failed - Если одно или оба поля пусты, WordPress генерирует
объект ошибки в качестве первого параметра фильтра authenticate;
при этом не срабатывает хук
wp_login_failed, который мог бы перехватить данное событие
Вот что мы сделали (см. комментарии в коде):
add_filter( 'authenticate', function( $user, $username, $password ) {
// Принудительно перехватываем неудачную попытку входа, чтобы активировать действие wp_login_failed,
// позволяя таким образом перехватить это событие
if ( empty( $username ) || empty( $password ) ) {
do_action( 'wp_login_failed', $user );
}
return $user;
}, 10, 3 );
// Для обработки события можно использовать следующий код:
add_action( 'wp_login_failed', function( $username ) {
if ( is_wp_error( $username ) ) {
// выполнить операции с объектом ошибки для случая пустых полей
}
} );
Проголосовал как за хороший ответ, но, пожалуйста, исправьте проблему с хуком authenticate. Без указания приоритета и количества аргументов это всегда будет вызывать действие неудачного входа из-за того, как работают фильтры и хуки (по умолчанию они передают только один аргумент в метод).
Tom Dyer
Параметр $username в wp_login_failed является строкой согласно документации разработчика для wp_login_failed - как теперь получить объект ошибки из него?
Cush