Over a million developers have joined DZone.

Overriding Tomcat Valve To Return Extended Login Failure Status

·
See Shade Grown Code for more information.

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);
    }
}
Topics:

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}