Spring Security AuthenticationFailureHandler vs AuthenticationFailureEvent

I work with spring-security 3.1.4 and I have requirements:

  • Track authentication success or failure
    • if success puts general user information in session attributes
    • if the result is unsuccessful;
      • determine the reason for the failure (the account is blocked, the account has expired, the authorization has expired, the user is disconnected, login attempts are exceeded, etc.)
      • Generate login error message for growl component in login.xhtml file
      • Take an action specific to the failure event, for example. if the credentials fail, they increase login attempts in db and / or redirect to the page, for example, overriding credentials

I researched and found three solutions for this:

  • PhaseListener implementation that is PhaseListener because it is called in
    all phase events:

 public class LoginErrorPhaseListener implements PhaseListener { private static final long serialVersionUID = -404551400448242299L; private static final String MESSAGES_RESOURCE_BUNDLE_NAME = "msgs"; private static final String ACCESS_DENIED_MESSAGE_KEY = "accessDeniedMessage"; private static final String BAD_CREDENTIALS_MESSAGE_KEY = "badCredentialsMessage"; @Override public void beforePhase(final PhaseEvent arg0) { Exception e = (Exception) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(WebAttributes.AUTHENTICATION_EXCEPTION); if (e instanceof BadCredentialsException) { FacesContext fc = FacesContext.getCurrentInstance(); ResourceBundle messages = fc.getApplication().getResourceBundle(fc, MESSAGES_RESOURCE_BUNDLE_NAME); fc.getExternalContext().getSessionMap().put(WebAttributes.AUTHENTICATION_EXCEPTION, null); fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, messages.getString(ACCESS_DENIED_MESSAGE_KEY), messages.getString(BAD_CREDENTIALS_MESSAGE_KEY))); } } @Override public void afterPhase(final PhaseEvent arg0) { } @Override public PhaseId getPhaseId() { return PhaseId.RENDER_RESPONSE; } } 
  • Other configures AuthenticationFailureHandler and AuthenticationSuccessHandler

 public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Inject private UserDao userDao; public CustomAuthenticationFailureHandler() { } public CustomAuthenticationFailureHandler(String defaultFailureUrl) { super(defaultFailureUrl); } @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { super.onAuthenticationFailure(request, response, exception); Class exceptionClazz = exception.getClass(); if (exceptionClazz == UsernameNotFoundException.class) { } else if (exceptionClazz == AuthenticationCredentialsNotFoundException.class) { } else if (exceptionClazz == BadCredentialsException.class) { UserBean user = (UserBean) exception.getExtraInformation(); if (user.getLoginAttempts() == 2) { userDao.updateUserStates(user.getUsername(), true, false, true, true); userDao.resetUserLoginFailedAttempts(user.getUsername()); } else { userDao.incrementLoginFailedAttempts(user.getUsername()); } } else if (exceptionClazz == AccountStatusException.class) { } else if (exceptionClazz == AuthenticationServiceException.class) { } else if (exceptionClazz == InsufficientAuthenticationException.class) { } else if (exceptionClazz == NonceExpiredException.class) { } else if (exceptionClazz == PreAuthenticatedCredentialsNotFoundException.class) { } else if (exceptionClazz == ProviderNotFoundException.class) { } else if (exceptionClazz == RememberMeAuthenticationException.class) { } else if (exceptionClazz == SessionAuthenticationException.class) { } } } public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Inject private UserDao userDao; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { super.onAuthenticationSuccess(request, response, authentication); UserPersonalInfoBean activeUser = (UserPersonalInfoBean) authentication.getPrincipal(); request.getSession().setAttribute("activeUser", activeUser); userDao.resetUserLoginFailedAttempts(activeUser.getUsername()); } } 
  • And the last way I found is to implement spring -context's ApplicationListener

 @Named public class BadCredentialsListener implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> { private static final long serialVersionUID = -404551400448242299L; private static final String MESSAGES_RESOURCE_BUNDLE_NAME = "msgs"; private static final String ACCESS_DENIED_MESSAGE_KEY = "accessDeniedMessage"; private static final String BAD_CREDENTIALS_MESSAGE_KEY = "badCredentialsMessage"; @Override public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent event) { FacesContext fc = FacesContext.getCurrentInstance(); ResourceBundle messages = fc.getApplication().getResourceBundle(fc, MESSAGES_RESOURCE_BUNDLE_NAME); fc.getExternalContext().getSessionMap().put(WebAttributes.AUTHENTICATION_EXCEPTION, null); fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, messages.getString(ACCESS_DENIED_MESSAGE_KEY), messages.getString(BAD_CREDENTIALS_MESSAGE_KEY))); } } 

My question is finally here. I am a junior developer and could not / understood how efficient / short-term effective is the best way to overcome my requirements and the technologies that I used (jsr330 injection, jsf context, etc.).

I refuse the jsf PhaseListener for the reason I mentioned above. In fact, spring-security access and fault handlers are similar to PhaseListener , but more efficient because they are called in more specific conditions. More specific events should be selected from the exception by type. However, I have to agree that when you define them in security-context.xml readability of the security module increases. Hearing AbstractAuthenticationFailureEvent child classes look really wonderful. Each event is vertically separated from each other and cleared. Also, since the AuthenticationException getExtraInformation and getAuthentication are deprecated, I could not find another way to find the username with the unsuccessful name in AuthenticationFailureHandler.onAuthenticationFailure .

So, guys, as you know, I'm pretty confused and open to your comments.

Thanks in advance, Relationship

+4
source share
1 answer

I think the decision on whether to use one or the other option depends on your requirements.

for example, one motivation for using handlers: handlers get request and response parameters. therefore, if you want to redirect the user to a page in some cases (for example, if the account is locked and you want to show him another HTML page), you should use handlers. listener events cannot (and should not) redirect users or change the flow. they are just listeners ...

0
source

Source: https://habr.com/ru/post/1499019/


All Articles