JASPIC Wildfly 9 validateRequest с сеансом

На основе этого примера Jaspic я написал следующее validateRequest метод для ServerAuthModule:

public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
        Subject serviceSubject) throws AuthException {

    boolean authenticated = false;
    final HttpServletRequest request = 
                      (HttpServletRequest) messageInfo.getRequestMessage();
    final String token = request.getParameter("token");
    TokenPrincipal principal = (TokenPrincipal) request.getUserPrincipal();

    Callback[] callbacks = new Callback[] {
            new CallerPrincipalCallback(clientSubject, (TokenPrincipal) null) };

    if (principal != null) {
        callbacks = new Callback[] { 
                new CallerPrincipalCallback(clientSubject, principal) };
        authenticated = true;
    } else {
        if (token != null && token.length() == Constants.tokenLength) {
            try {
                principal = fetchUser(token);
            } catch (final Exception e) {
                throw (AuthException) new AuthException().initCause(e);
            }
            callbacks = new Callback[]
                        { 
                             new CallerPrincipalCallback(clientSubject, principal),
                             new GroupPrincipalCallback(clientSubject,
                                                        new String[] { "aRole" })
                        };
            messageInfo.getMap().put("javax.servlet.http.registerSession", "TRUE");
            authenticated = true;
        }
    }

    if (authenticated) {
        try {
            handler.handle(callbacks);
        } catch (final Exception e) {
            throw (AuthException) new AuthException().initCause(e);
        }
        return SUCCESS;
    }

    return AuthStatus.SEND_FAILURE;
}

Это работает, как и ожидалось, для первого вызова EJB с @RolesAllowed("aRole") но для следующего звонка это не работает вообще. Wildfly отрицает это с этим сообщением об ошибке:

ERROR [org.jboss.as.ejb3.invocation] (default task-4) WFLYEJB0034: EJB Invocation 
    failed on component TestEJB for method public java.lang.String 
    com.jaspic.security.TestEJB.getPrincipalName():
    javax.ejb.EJBAccessException: WFLYSEC0027: Invalid User

Если я угадаю правильно, ошибка происходит в:org.jboss.as.security.service.SimpleSecurityManager строка 367 исходного кода wilfly из-за строки 405, в которой credential проверено, но, кажется, null,

Это похоже на Wildfly 8/9/10CR (другие версии не тестировались).

Опять же, я не уверен, что я делаю это неправильно или это та же ошибка, что и https://issues.jboss.org/browse/WFLY-4626? И это вообще ошибка, или это ожидаемое поведение?

1 ответ

Решение

Это звучит как ошибка для меня, а также личность вызывающего абонента (звонящий / группа Principal s) кажется сохраненным при последующих вызовах к сети, но не к контейнеру EJB. Мои собственные классы JASPIC (которые правильно работают в GlassFish 4.1) не работают по той же причине в WildFly 9.0.2.Final и 10.0.0.CR4 при использовании вместе с простым сервлетом и SLSB, даже если последний отмечен @PermitAll,

Поскольку я сам незнаком с внутренностями безопасности WildFly, я не могу помочь вам в этом. Если вы не сможете исправить это, единственный обходной путь на уровне SAM, о котором я могу подумать, в настоящее время состоит в том, чтобы не использовать javax.servlet.http.registerSession свойство обратного вызова, которое, казалось бы, вызывает проблему, но вместо этого имеет CallbackHandler зарегистрировать оба абонента Principal и его группы на каждом validateRequest(...) призывание. Если это применимо к вашему случаю использования, вы можете приложить эту информацию к HttpSession чтобы немного ускорить процесс; в противном случае повторите с нуля. Так, например:

public class Sam implements ServerAuthModule {

    // ...

    @Override
    public AuthStatus validateRequest(MessageInfo mi, Subject client, Subject service) throws AuthException {
        boolean authenticated = false;
        boolean attachAuthnInfoToSession = false;
        final String callerSessionKey = "authn.caller";
        final String groupsSessionKey = "authn.groups";
        final HttpServletRequest req = (HttpServletRequest) mi.getRequestMessage();
        TokenPrincipal tp = null;
        String[] groups = null;
        String token = null;
        HttpSession hs = req.getSession(false);
        if (hs != null) {
            tp = (TokenPrincipal) hs.getAttribute(callerSessionKey);
            groups = (String[]) hs.getAttribute(groupsSessionKey);
        }
        Callback[] callbacks = null;
        if (tp != null) {
            callbacks = new Callback[] { new CallerPrincipalCallback(client, tp), new GroupPrincipalCallback(client, groups) };
            authenticated = true;
        }
        else if (isValid(token = req.getParameter("token"))) {
            tp = newTokenPrincipal(token);
            groups = fetchGroups(tp);
            callbacks = new Callback[] { new CallerPrincipalCallback(client, tp), new GroupPrincipalCallback(client, groups) };
            authenticated = true;
            attachAuthnInfoToSession = true;
        }
        if (authenticated) {
            try {
                handler.handle(callbacks);
                if (attachAuthnInfoToSession && ((hs = req.getSession(false)) != null)) {
                    hs.setAttribute(callerSessionKey, tp);
                    hs.setAttribute(groupsSessionKey, groups);
                }
            }
            catch (IOException | UnsupportedCallbackException e) {
                throw (AuthException) new AuthException().initCause(e);
            }
            return AuthStatus.SUCCESS;
        }
        return AuthStatus.SEND_FAILURE;
    }

    // ...

    @Override
    public void cleanSubject(MessageInfo mi, Subject subject) throws AuthException {
        // ...
        // just to be safe
        HttpSession hs = ((HttpServletRequest) mi.getRequestMessage()).getSession(false);
        if (hs != null) {
            hs.invalidate();
        }
    }

    private boolean isValid(String token) {
        // whatever
        return ((token != null) && (token.length() == 10));
    }

    private TokenPrincipal newTokenPrincipal(String token) {
        // whatever
        return new TokenPrincipal(token);
    }

    private String[] fetchGroups(TokenPrincipal tp) {
        // whatever
        return new String[] { "aRole" };
    }

}

Я протестировал вышеупомянутое на вышеупомянутых версиях WildFly и вышеупомянутым способом (то есть с единственным сервлетом, ссылающимся на один SLSB, помеченный @DeclareRoles / уровень метода @RolesAllowed) и похоже на работу как положено. Очевидно, я не могу гарантировать, что этот подход не потерпит неудачу другими неожиданными способами.


Смотрите также:

Другие вопросы по тегам