Cum să redirecționezi utilizatorul folosind hook-ul 'wp_login_failed' dacă eroarea este 'empty_username' sau 'empty_password'
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
authenticatese găsește în funcțiawp_authenticate() - Hook-ul
wp_login_failedse găsește în funcțiawp_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?
WordPress gestionează eșecul la autentificare în două moduri:
- 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 - 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_failedpentru 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ă
}
} );
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
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