Reindirizzare l'utente usando l'hook 'wp_login_failed' se l'errore è 'empty_username' o 'empty_password'
Contesto
Sto cercando di creare un login frontend senza interruzioni per un blog, mantenendo comunque disponibile wp-login.php per gli utenti che sanno come accedervi. wp-login.php è anche necessario per autenticare i login frontend, quindi non può essere rimosso completamente.
Ci sono quasi riuscito, ma ho un problema con i login falliti, in particolare con un username vuoto.
Domanda
Qualcuno conosce un modo per evitare che wp-login.php venga visualizzato per errori di username/password vuoti, evitando al contempo che venga mostrato un errore quando un utente accede direttamente a wp-login.php?
Il mio lavoro
Ho individuato l'hook wp_login_failed e creato la funzione seguente. In pratica aggiunge parametri alla query per indicare che il login è fallito e quale username è stato inserito, prima di reindirizzare l'utente alla pagina di login frontend. Con questi parametri posso determinare perché il login è fallito e mostrare un messaggio all'utente.
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;
}
Tuttavia, ho notato che se il campo username era vuoto non venivo reindirizzato e all'utente veniva ancora mostrato wp-login.php.
Analizzando meglio ho trovato la funzione wp_authenticate() dove ho notato che se il primo errore di login era empty_username o empty_password, l'hook wp_login_failed non veniva attivato. Per ovviare a questo, ho utilizzato l'hook filter authenticate modificando il codice di questi errori, anteponendo djg_ a loro, in modo che non venissero ignorati.
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;
}
Questo funziona, ma ora se l'utente accede direttamente alla pagina wp-login.php, che deve rimanere disponibile (come descritto nel Contesto), viene visualizzato un errore -
ERRORE: Il campo username è vuoto.
ERRORE: Il campo password è vuoto.
Infine, dopo molte difficoltà, ho trovato nella funzione wp_signon() il codice che causava questo comportamento, ma non vedo alcun filtro che permetta di modificarlo. In pratica dice che se gli errori sono esattamente empty_username e empty_password, ignorarli. -
if ( is_wp_error($user) ) {
if ( $user->get_error_codes() == array('empty_username', 'empty_password') ) {
$user = new WP_Error('', '');
}
return $user;
}
Dettagli
wp_authenticate()si trova in/wp-includes/pluggable.php- L'hook filter
authenticatesi trova all'interno della funzionewp_authenticate() - L'hook action
wp_login_failedsi trova all'interno della funzionewp_authenticate() wp_signon()si trova in/wp-includes/user.php
Domanda (di nuovo)
Qualcuno conosce un modo per evitare che wp-login.php venga visualizzato per errori di username vuoti, evitando al contempo che venga mostrato un errore quando un utente accede direttamente a wp-login.php?
WordPress gestisce il login fallito in due modi:
- Se si tratta di credenziali errate e sia username che password hanno un valore, allora questa azione può essere catturata da
wp_login_failed - Se entrambi o uno dei campi sono vuoti, WordPress genera l'oggetto errore come primo parametro nel filtro authenticate; non attiva e l'azione
wp_login_failednon cattura questa causa/evento
Per quello che abbiamo fatto qui, vedi i commenti nel codice:
add_filter( 'authenticate', function( $user, $username, $password ) {
// forza la cattura del login fallito per attivare forzatamente l'azione wp_login_failed,
// in modo che questo evento possa essere catturato
if ( empty( $username ) || empty( $password ) ) {
do_action( 'wp_login_failed', $user );
}
return $user;
}, 10, 3 );
// per gestire l'evento puoi gestire l'errore come
add_action( 'wp_login_failed', function( $username ) {
if ( is_wp_error( $username ) ) {
// esegui operazioni sull'oggetto errore per l'errore vuoto
}
} );
Upvotato come buona risposta, ma per favore correggi il problema con l'hook authenticate. Senza passare una priorità e un numero di argomenti, questo sempre attiverà un'azione di login fallito a causa del modo in cui funzionano i filtri e gli hook (per impostazione predefinita, consentono solo un argomento da passare al metodo).
Tom Dyer
Il parametro $username in wp_login_failed è una stringa secondo la documentazione per sviluppatori di wp_login_failed - come otteniamo ora un oggetto di errore da esso?
Cush