↓ Skip to main content

Programmatic J2EE authentication in Tomcat

·371 words·2 mins
Sergii Zolotarov
Author
Sergii Zolotarov
Twenty years of Java, from Kyiv to Leeuwarden. Currently at Booking.com

I guess everybody who worked with web J2EE Security regretted about extreme inflexibility of authentication web tier. Yes, the technology is good and flexible, you can for example stick a JAAS module which will work in any environment and the programmer will not need to mess up with login forms and page protection from one project to another. Yet, what will you tell your customer if he asks for example that after registration user is immediately logged in implicitly? How do you make Tomcat log in a user without showing authentication form or hitting a secured url?

Using built-in J2EE APIs you can create a new LoginContext and invoke a login() method. However the web container does not know a thing about the LoginContext and HttpServletRequest.isUserInRole() will repeatedly say false.

I found a quick and dirty solution, which can be used all alone or together with form based authentication, sorry only in Tomcat. It programmatically “logs in” a user with a set of roles:


package com.anydoby.login;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.authenticator.AuthenticatorBase;
import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.Response;
import org.apache.catalina.connector.ResponseFacade;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.realm.GenericPrincipal;

/**
 * 
 * @author SergeyZ
 *
 */
public class ProgrammaticAuthenticator extends AuthenticatorBase {

    private Field request;
    private Field response;

    {
        Field[] fields = RequestFacade.class.getDeclaredFields();
        request = filter(fields, "request");
        request.setAccessible(true);
        fields = ResponseFacade.class.getDeclaredFields();
        response = filter(fields, "response");
        response.setAccessible(true);
    }
    
    public void authenticate(HttpServletRequest rq, HttpServletResponse rs, String user, String password, List<String> roles) {        
        try {
            GenericPrincipal principal = new GenericPrincipal(null, user, password, roles);
            register((Request)request.get(rq), (Response)response.get(rs), principal, Constants.FORM_METHOD, user, password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    private Field filter(Field[] fields, String name) {
        for (Field field : fields) {
            if (field.getName().equals(name)) {
                return field;
            }
        }
        return null;
    }

    @Override
    protected boolean authenticate(Request request, Response response, LoginConfig config) throws IOException {
        throw new UnsupportedOperationException("Use authenticate(Principal)");
    }

}

After you have programmatically defined that user credentials are correct and have received his roles you call authenticate() and Tomcat will memorize the person. Don’t please blame me for using reflections, Tomcat authors left no choice :)

Here is a little sample which you can use to play around with the concept.


First published on the old anydoby.com blog on 2008-11-17.