Перенаправление пользователя с помощью хука 'wp_login_failed' при ошибках 'empty_username' или 'empty_password'

18 дек. 2014 г., 12:20:18
Просмотры: 16.7K
Голосов: 7

Предыстория

Я пытаюсь создать удобный фронтенд-логин для блога, оставляя при этом доступ к 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?

0
Все ответы на вопрос 1
3

WordPress обрабатывает неудачные попытки входа двумя способами:

  1. Если введены неверные учетные данные, и оба поля - имя пользователя и пароль - содержат значения, то это событие может быть перехвачено хуком wp_login_failed
  2. Если одно или оба поля пусты, 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 ) ) {
        // выполнить операции с объектом ошибки для случая пустых полей
    }
} );
4 апр. 2015 г. 10:53:39
Комментарии

Проголосовал как за хороший ответ, но, пожалуйста, исправьте проблему с хуком authenticate. Без указания приоритета и количества аргументов это всегда будет вызывать действие неудачного входа из-за того, как работают фильтры и хуки (по умолчанию они передают только один аргумент в метод).

Tom Dyer Tom Dyer
6 мар. 2018 г. 14:33:27

Параметр $username в wp_login_failed является строкой согласно документации разработчика для wp_login_failed - как теперь получить объект ошибки из него?

Cush Cush
15 сент. 2018 г. 04:19:47

@TomDyer прав, исходя из вопроса, правильное утверждение должно быть add_filter( 'authenticate', function(...) {...}, 99, 3);

GigiSan GigiSan
17 окт. 2018 г. 12:58:25