Cum să redirecționezi utilizatorul folosind hook-ul 'wp_login_failed' dacă eroarea este 'empty_username' sau 'empty_password'

18 dec. 2014, 12:20:18
Vizualizări: 16.7K
Voturi: 7

Context

Încerc să implementez un sistem de autentificare transparent pe frontend pentru un blog, păstrând în același timp accesibil wp-login.php pentru utilizatorii care știu cum să ajungă acolo. wp-login.php este necesar și pentru autentificarea pe frontend, așa că nu poate fi eliminat complet.

Am aproape totul funcțional, dar am o problemă cu erorile de autentificare, în special când câmpul de username este gol.

Problemă

Există o metodă de a preveni afișarea paginii wp-login.php pentru erorile de username/parolă goale, evitând în același timp afișarea erorii când un utilizator accesează direct wp-login.php?

Soluția mea

Am identificat hook-ul wp_login_failed și am creat funcția de mai jos. Practic, adaugă parametri în URL care indică că autentificarea a eșuat și ce username a fost introdus, apoi redirecționează utilizatorul înapoi pe pagina de login de pe frontend. Cu acești parametri pot determina motivul eșuării și afișa un mesaj utilizatorului.

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;

}

Totuși, am observat că dacă câmpul de username era gol, nu eram redirecționat și utilizatorul vedea în continuare pagina wp-login.php.

După investigații am descoperit funcția wp_authenticate() unde am observat că dacă prima eroare de login era empty_username sau empty_password, hook-ul wp_login_failed nu era declanșat. Pentru a rezolva, am folosit filtrul authenticate și am modificat codul acestor erori, prefixându-le cu djg_ pentru a nu fi ignorate.

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;

}

Această soluție funcționează, dar acum dacă utilizatorii accesează direct pagina wp-login.php, care trebuie să rămână disponibilă (cum am menționat în Context), se afișează o eroare -

EROARE: Câmpul de username este gol.
EROARE: Câmpul de parolă este gol.

În final, după multe încercări, am găsit în funcția wp_signon() codul care cauza această problemă, dar nu văd niciun filtru care să-mi permită să modific acest comportament. Practic, codul spune că dacă erorile sunt exact empty_username și empty_password, să fie ignorate:

if ( is_wp_error($user) ) {
    if ( $user->get_error_codes() == array('empty_username', 'empty_password') ) {
        $user = new WP_Error('', '');
    }

    return $user;
}

Detalii

  • wp_authenticate() se află în /wp-includes/pluggable.php
  • Filtrul authenticate se găsește în funcția wp_authenticate()
  • Hook-ul wp_login_failed se găsește în funcția wp_authenticate()
  • wp_signon() se află în /wp-includes/user.php

Problemă (repetare)

Există vreo metodă de a preveni afișarea paginii wp-login.php pentru erorile de username gol, evitând în același timp afișarea erorii când un utilizator accesează direct wp-login.php?

0
Toate răspunsurile la întrebare 1
3

WordPress gestionează eșecul la autentificare în două moduri:

  1. Dacă credențialele sunt greșite, iar atât numele de utilizator cât și parola au valori, atunci această acțiune poate fi capturată prin wp_login_failed
  2. Dacă ambele sau una dintre opțiuni sunt goale, atunci WordPress generează obiectul de eroare ca prim parametru în filtrul authenticate; nu declanșează acțiunea wp_login_failed pentru a captura această cauză/eveniment

Pentru ceea ce am făcut aici, vezi comentariile în cod:

add_filter( 'authenticate', function( $user, $username, $password ) {
    // capturăm forțat eșecul la autentificare pentru a declanșa acțiunea wp_login_failed,
    // astfel încât acest eveniment să poată fi capturat
    if ( empty( $username ) || empty( $password ) ) {
        do_action( 'wp_login_failed', $user );
    }
    return $user;
}, 10, 3 );

// pentru a gestiona evenimentul, poți gestiona eroarea astfel:
add_action( 'wp_login_failed', function( $username ) {
    if ( is_wp_error( $username ) ) {
        // efectuează operații pe obiectul de eroare pentru eroarea goală
    }
} );
4 apr. 2015 10:53:39
Comentarii

Apreciat ca un răspuns bun, dar te rog corectează problema cu hook-ul de autentificare. Fără a specifica o prioritate și numărul de argumente, acesta va declanșa întotdeauna o acțiune de autentificare eșuată din cauza modului în care funcționează filtrele și hook-urile (în mod implicit, ele permit doar un singur argument să fie transmis metodei).

Tom Dyer Tom Dyer
6 mar. 2018 14:33:27

Parametrul $username din wp_login_failed este un string conform documentației pentru dezvoltatori pentru wp_login_failed - cum obținem acum un obiect de eroare din el?

Cush Cush
15 sept. 2018 04:19:47

@TomDyer are dreptate, deducând din întrebare afirmația corectă ar trebui să fie add_filter( 'authenticate', function(...) {...}, 99, 3);

GigiSan GigiSan
17 oct. 2018 12:58:25