2015-05-16 17 views
9

Desidero registrare se un utente tenta di autenticare con credenziali errate. Quindi ho aggiunto questa classe listener di eventi al mio progetto:Spring BadCredentials Evento non attivo

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.context.ApplicationListener; 
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent; 
import org.springframework.stereotype.Component; 

@Component 
public class AuthenticationFailureListener 
     implements ApplicationListener<AuthenticationFailureBadCredentialsEvent>{ 

    private final Logger logger = LoggerFactory.getLogger(getClass()); 

    @Override 
    public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent event) { 
     System.out.println("test"); 
     logger.info("test2"); 
    } 
} 

Problema è che non funziona affatto. Uso la pagina di accesso predefinita di Spring Security. La pagina mostra l'errore "cattive credenziali" quando si utilizzano credenziali errate, ma il mio metodo sopra non viene richiamato. Ho codice molto simile per un ascoltatore successo dell'evento, che funziona a meraviglia:

@Component 
public class AuthenticationSuccessListener implements 
     ApplicationListener<InteractiveAuthenticationSuccessEvent> { 

    private final Logger logger = LoggerFactory.getLogger(getClass()); 

    @Autowired private UserService users; 

    @Override 
    public void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) { 
     User user = users.get(event.getAuthentication().getName()); 
     boolean isAdmin = user.getRole().equals(User.ROLE_ADMIN); 
     logger.info((isAdmin ? "Admin" : "User") + " with id " + user.getIdLink() 
       + " has successfully logged in!"); 
    } 
} 

Ecco la mia Primavera configurazione della protezione Java:

@Configuration 
@EnableWebMvcSecurity 
@EnableGlobalMethodSecurity(prePostEnabled=true) 
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{ 

    @Autowired 
    private CustomUserDetailsService userDetailsService; 

    @Bean 
    public PasswordEncoder passwordEncoder() { 
     return new BCryptPasswordEncoder(); 
    } 

    @Autowired 
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 
     auth 
      .userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); 
    } 

    protected void configure(HttpSecurity http) throws Exception { 
     http 
      .csrf().disable() 
      .formLogin() 
       .and() 
      .httpBasic(); 
    } 
} 

non ho idea di che cosa sta succedendo qui, l'aiuto ha apprezzato un sacco! Versione

Primavera: 4.0.9
Primavera di sicurezza Versione: 3.2.5 (anche provato 4.0.1)


Edit:

Va bene, ho impostato il livello di log per eseguire il debug per la primavera, ma niente. Ho cercato ogni occorrenza di "Listener" e il log afferma che le istanze di AuthenticationFailureListener e AuthenticationSuccessListeners sono state create senza errori.

Ho persino inserito il registro nello strumento diff (dopo aver sostituito tutte le volte & censura) e confrontato con una versione di codice in cui il codice FailureListener è stato commentato, ma non ha trovato nulla. Puoi cercarlo da solo se vuoi:
https://www.diffchecker.com/cwdn4sp4
Nella parte inferiore della pagina troverai il testo del registro semplice sul lato sinistro.


Edit2: parzialmente risolto

soluzione Serges aiutato, ecco la mia completa implementazione del metodo onAuthenticationFailure:

@Override 
public void onAuthenticationFailure(
     HttpServletRequest request, HttpServletResponse response, 
     AuthenticationException exception) throws IOException, ServletException { 

    if (exception instanceof BadCredentialsException) { 
     String name = request.getParameter("username"); 
     String password = request.getParameter("password"); 
     Authentication auth = 
       new UsernamePasswordAuthenticationToken(name, password); 
     eventPublisher.publishEvent(
       new AuthenticationFailureBadCredentialsEvent(auth, exception)); 
    } 
    super.onAuthenticationFailure(request, response, exception); 
} 
+0

Possiedi una corretta scansione componente impostato in modo che il vostro 'AuthenticationFailureListener' viene creato come un fagiolo? Abilitare la registrazione debug Spring per verificare che sia stata effettivamente creata. Questo ti darà qualche informazione in più con cui lavorare. –

+0

FailureListener si trova nello stesso pacchetto di SuccessListener, pertanto la scansione dovrebbe funzionare con entrambi. Mi piacerebbe provare la registrazione di debug ora, grazie! – Katharsas

risposta

6

Questo è alla progettazione.

Javadoc per AbstractAuthenticationProcessingFilter è chiara su questo:

evento di pubblicazione:

Se l'autenticazione è riuscita, un InteractiveAuthenticationSuccessEvent verrà pubblicato tramite il contesto dell'applicazione. Nessun evento verrà pubblicato se l'autenticazione non è andata a buon fine, poiché in genere ciò viene registrato tramite un evento dell'applicazione specifico di AuthenticationManager.

(sottolineare il mio)

Se si desidera inviare esplicitamente un evento per errori di autenticazione, è possibile utilizzare un costume AuthenticationFailureHandler estende SimpleUrlAuthenticationFailureHandler che avrebbe inviato l'evento e chiamare classe base onAuthenticationFailure metodo.

public class EventSendingAuthenticationFailureHandler 
     extends SimpleUrlAuthenticationFailureHandler, 
     implements ApplicationEventPublisherAware { 

    protected ApplicationEventPublisher eventPublisher; 

    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) { 
     this.eventPublisher = eventPublisher; 
    } 

    @Override 
    void onAuthenticationFailure(javax.servlet.http.HttpServletRequest request, 
         javax.servlet.http.HttpServletResponse response, 
         AuthenticationException exception) 
         throws IOException, 
           javax.servlet.ServletException { 
     // use eventPublisher to publish the event according to exception 
     super.onAuthenticationFailure(request, response, exception); 
    } 
} 

Si dovrebbe essere in grado di configurare in questo modo:

@Bean 
AuthenticationFailureHandler eventAuthenticationFailureHandler() { 
    return new EventSendingAuthenticationFailureHandler(); 
} 

@Autowired 
AuthenticationFailureHandler eventAuthenticationFailureHandler; 

protected void configure(HttpSecurity http) throws Exception { 
    http 
     .csrf().disable() 
     .formLogin().failureHandler(eventAuthenticationFailureHandler) 
      .and() 
     .httpBasic(); 
} 
+0

C'è ancora un problema: '.formLogin(). FailureHandler (gestore)' interrompe il reindirizzamento della pagina di accesso generato su cattive credenziali (anche se chiamo 'super ("/login? Errore ")' nel mio addetto ai gestori). Porta a "Nessuna mappatura trovata per richiesta HTTP con URI [/ GMM/login] in DispatcherServlet con il nome" GMM "e viene restituita una pagina 404 quando si utilizzano credenziali errate. – Katharsas

+0

Penso che sia solo un problema di configurazione del gestore. Qual è l'URL della tua pagina di accesso? Cosa succede se il tuo tipo quell'URL direttamente nel tuo browser ('http: // host/context/xxx'), e lo stesso con'? Error' alla fine? –

+0

L'URL della pagina di accesso è attualmente http: // localhost: 8080/GMM/login La digitazione diretta non modifica alcun comportamento./login? errore restituisce un 404 in tutti i casi. – Katharsas

9

ho ottenuto questo lavoro in modo diverso.

@Configuration 
@EnableWebMvcSecurity 
@EnableGlobalMethodSecurity(prePostEnabled=true) 
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{ 

    // Inject applicationEventPublisher 
    @Inject 
    private ApplicationEventPublisher applicationEventPublisher; 

    @Autowired 
    private CustomUserDetailsService userDetailsService; 

    @Bean 
    public PasswordEncoder passwordEncoder() { 
     return new BCryptPasswordEncoder(); 
    } 

    @Autowired 
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 
     auth 
      // configure a auth event publisher 
      .authenticationEventPublisher(new DefaultAuthenticationEventPublisher(applicationEventPublisher)) 
      .userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); 
    } 

    protected void configure(HttpSecurity http) throws Exception { 
     http 
      .csrf().disable() 
      .formLogin() 
       .and() 
      .httpBasic(); 
    } 
} 

Con queste modifiche, il mio listener di eventi è stato in grado di ricevere eventi di errore di autenticazione. Questo è con spring-security 4.0.2.RELEASE e spring-boot 1.2.5.RELEASE

Spero che aiuti.

+0

puoi per favore postare l'intero esempio? – george

+0

È possibile utilizzare JHipster (http://jhipster.github.io/) per generare un'app campione. – krishnakumarp

0

Il LoggerListener funziona correttamente con Spring Security 3.2.8. Vedi Grepcode per il codice sorgente.

Il codice adpoted:

@Named 
public class AuthenticationSuccessListener implements ApplicationListener<AbstractAuthenticationEvent> { 

    private final Logger logger = LoggerFactory.getLogger(getClass()); 

    @Inject 
    private UserService users; 

    @Override 
    public void onApplicationEvent(AbstractAuthenticationEvent event) { 

     if (event instanceof InteractiveAuthenticationSuccessEvent) { 

      User user = users.get(event.getAuthentication().getName()); 
      boolean isAdmin = user.getRole().equals(User.ROLE_ADMIN); 
      logger.info((isAdmin ? "Admin" : "User") + " with id " + user.getIdLink() + " has successfully logged in!"); 
     } 
    } 
}