Note that there are some explanatory texts on larger screens.

plurals
  1. POSpring Security AuthenticationFailureHandler vs AuthenticationFailureEvent
    primarykey
    data
    text
    <p>i am working with spring-security 3.1.4 and i have requirements:</p> <ul> <li>Watch over whether the authentication is successful or failure <ul> <li>if success put user general info into the session attributes</li> <li>if the outcome is failure then; <ul> <li>determine the cause of failure (account locked, account expired, credentials expired, user disabled, login failure attempts exceed and such)</li> <li>Generate login failure message for the growl component resides in the login.xhtml</li> <li>Take action specific to the failure event e.g. on bad credentials increment login failure attempts in db and/or redirect to a page like re-defining credentials</li> </ul></li> </ul></li> </ul> <p>I have researched and found out three solutions for that:</p> <ul> <li>Implementing <code>PhaseListener</code> which is sloppy cause it is called in<br> all phase events:</li> </ul> <p></p> <pre><code>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; } } </code></pre> <ul> <li>Other is customizing <code>AuthenticationFailureHandler</code> and <code>AuthenticationSuccessHandler</code></li> </ul> <p></p> <pre><code>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()); } } </code></pre> <ul> <li>And the last way that i found is implementing spring-context's <code>ApplicationListener</code></li> </ul> <p></p> <pre><code>@Named public class BadCredentialsListener implements ApplicationListener&lt;AuthenticationFailureBadCredentialsEvent&gt; { 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))); } } </code></pre> <p>My question is finally here. I am a junior developer and cant desided/figured it out which way is the effective/efficient for short the best way to overcome my requirements and technologies i used (jsr330 injection, jsf context and such). </p> <p>I give up the jsf <code>PhaseListener</code> solution the reason i stated above. Actually spring-security access and failure handlers are similar to <code>PhaseListener</code>s but more efficient because they are called on more specific conditions. More specific events have to be picked up from exception based on type of it. However, I should agree that in defining them in <code>security-context.xml</code> increases readability of the security module. Listening <code>AbstractAuthenticationFailureEvent</code> child classes looks really fine by me. Each event is vertically seperated from each other and its clean. In addition, since <code>AuthenticationException</code>'s <code>getExtraInformation</code> and <code>getAuthentication</code> methods deprecated i could not able to find another way to reach failured user's username in <code>AuthenticationFailureHandler.onAuthenticationFailure</code>.</p> <p>So guys as you understand, i am pretty confused and open for your comments.</p> <p>Thank you in advance, Regards</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload