Русский English Тэги View Sergey Zolotaryov's profile on LinkedIn Вход
Программная аутентификация в Tomcat
Постоянная ссылка 17-11-2008 anydoby java

Наверное каждый, кто хоть раз сталкивался с веб J2EE Security, матюкался по поводу крайней негибкости сией архитектуры на уровне юзера. Да, технология хороша тем, что можно подсунуть, к примеру, JAAS модуль, который будет работать в любом контейнере, и программеру не нужно возиться каждый раз с защитой ресурсов и формами логина. Но, в то же время, что вы скажете заказчику, который хочет, чтобы после регистрации пользователь был авторизован автоматически? Как заставить Tomcat залогиниться, без показа формы авторизации?

Штатными методами J2EE можно лишь создать новый LoginContext и вызвать login(). Но веб контейнер ничего не знает про LoginContext и HttpServletRequest.isUserInRole() будет по-прежнему говорить false.

Я нашел простое и грязное решение, используя которое, и только на Tomcat(!), можно запомнить имя пользователя, его пароль и роли, ему принадлежащие:


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)");
    }

}

После того, как вы определили программно роли и имя/пароль пользователя, можно вызывать authenticate() и Tomcat чела запомнит в сессии. Попрошу не судить строго за использование рефлекций и низкие хаки, но, черт возьми, это хорошо работает :)

Зависимость проекта от catalina.jar можно убрать, если скомпилить ProgrammaticAuthenticator в отдельный jar и использовать, например, не класс, а интерфейс, и инстанциировать его по рефлекции (ну в общем чего мне вас учить).

Вот здесь пример, на котором можно поиграться.

Добавить комментарий

andreyn.gura
23-12-2008

Хороший блог, но очень не хватает RSS.

Предыдущая статья Преимущества генерированного кода на примере сортировки Следующая статья Работа с хранимыми процедурами в Toplink