ExtendedStatusSetter.java
package com.ofc.tomcat; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Interface flagging that the implementing Realm can set request * headers providing additional information about an authentication * failure. * * @author Nicholas Sushkin */ public interface ExtendedStatusSetter { /** * The request attribute under which we forward an extended failure status message * (as an object of type String) to a login error page. */ public static String LOGIN_FAILURE_MESSAGE_ATTR = "com.ofc.tomcat.LOGIN_FAILURE_MESSAGE"; public void setExtendedStatus(String username, HttpServletRequest request, HttpServletResponse response); }
ExtendedStatusFormAuthenticator.java
package com.ofc.tomcat; import org.apache.catalina.authenticator.Constants; import org.apache.catalina.authenticator.FormAuthenticator; import org.apache.catalina.Realm; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.deploy.LoginConfig; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.servlet.RequestDispatcher; /** * Adds extended authentication failure status to tomcat FormAuthenticator. * * @author Nicholas Sushkin */ public class ExtendedStatusFormAuthenticator extends FormAuthenticator { /** * Descriptive information about this implementation. */ protected static final String info = "com.ofc.tomcat.ExtendedStatusFormAuthenticator/1.0"; private static Log log = LogFactory.getLog(ExtendedStatusFormAuthenticator.class); // ------------------------------------------------------------- Properties /** * Return descriptive information about this Valve implementation. */ @Override public String getInfo() { return info; } // ------------------------------------------------------------- Overridden behavior /** * Called to forward to the error page * * @param request Request we are processing * @param response Response we are creating * @param config Login configuration describing how authentication * should be performed */ @Override protected void forwardToErrorPage(Request request, Response response, LoginConfig config) { Realm realm = context.getRealm(); if (realm instanceof ExtendedStatusSetter) { log.debug("realm implements ExtendedStatusSetter, setting extended status for error page"); String username = request.getParameter(Constants.FORM_USERNAME); ((ExtendedStatusSetter) realm).setExtendedStatus(username, request.getRequest(), response.getResponse()); } else { log.debug("realm does not implement ExtendedStatusSetter, NOT setting extended status for error page"); } RequestDispatcher disp = context.getServletContext().getRequestDispatcher (config.getErrorPage()); try { disp.forward(request.getRequest(), response.getResponse()); response.finishResponse(); } catch (Throwable t) { log.warn("Unexpected error forwarding to error page", t); } } }
Realm implementation will include the following
public class AccountLockoutDatasourceRealm extends DataSourceRealm implements ExtendedStatusSetter { // ... public void setExtendedStatus(String username, HttpServletRequest request, HttpServletResponse response) { setMessage(request, "Account locked"); } protected void setMessage(HttpServletRequest request, String message) { request.setAttribute(ExtendedStatusSetter.LOGIN_FAILURE_MESSAGE_ATTR, message); } }