Веб-приложение Java: использование настраиваемой области

Я пишу Java-приложение, которое необходимо выполнить вход через веб-сервис. Конечно, ни одна из областей, поставляемых с сервером приложений, который я использую (glassfish v2), не справится с задачей. Поэтому я должен был написать свой собственный. Однако кажется, что реализация области, которую я написал, полностью связана с Glassfish и не может использоваться, как и на любых других серверах приложений.

Существует ли какой-либо стандартный или широко поддерживаемый способ реализации пользовательского Царства? Возможно ли каким-либо образом развернуть эту область из.war, или ее всегда нужно загружать из собственного пути к классу сервера?

5 ответов

Решение

ПРИМЕЧАНИЕ. Приведенный ниже ответ действителен только для Java EE 5. Как я уже упоминал в одном из других ответов, Java EE 6 поддерживает это. Поэтому, если вы используете Java EE 6, не читайте этот ответ, а читайте другой соответствующий ответ.

Из моего собственного исследования и ответов на этот вопрос я нашел следующий ответ: хотя JAAS является стандартным интерфейсом, единого способа написания, развертывания и интеграции JAAS Realm + LoginModule на различных серверах приложений не существует.

Glassfish v2 требует от вас расширения некоторых собственных внутренних классов, которые сами реализуют LoginModule или Realm. Однако вы не можете настроить весь процесс входа в систему, потому что многие методы интерфейса LoginModule помечены как финальные в суперклассе Glassfish. Пользовательские классы LoginModule и Realm должны быть размещены в AS classpath (не в приложении), а область должна быть зарегистрирована вручную (развертывание из.war невозможно).

Ситуация выглядит немного лучше для Tomcat, который позволит вам полностью закодировать свои собственные Realm и LoginModule, а затем сконфигурировать их на сервере приложений, используя свой собственный JAASRealm (который делегирует реальную работу вашим реализациям Realm и LoginModule), Тем не менее, даже tomcat не позволяет развертывать ваше собственное царство из вашего.war.

Обратите внимание, что ни один из серверов приложений, которые показали мои результаты, по-видимому, не может в полной мере использовать все обратные вызовы JAAS. Кажется, что все они поддерживают только базовую схему имени пользователя и пароля. Если вам нужно что-то более сложное, чем это, вам нужно будет найти решение, которое не управляется вашим контейнером Java EE.

Для справки, и поскольку об этом просили в комментариях к моему вопросу, вот код, который я написал для GlassfishV2.

Прежде всего, вот реализация Realm:

public class WebserviceRealm extends AppservRealm {

private static final Logger log = Logger.getLogger(WebserviceRealm.class.getName());

private String jaasCtxName;
private String hostName;
private int port;
private String uri;

@Override
protected void init(Properties props) throws BadRealmException, NoSuchRealmException {
    _logger.info("My Webservice Realm : init()");

    // read the configuration properties from the user-supplied properties,
    // use reasonable default values if not present
    this.jaasCtxName = props.getProperty("jaas-context", "myWebserviceRealm");
    this.hostName = props.getProperty("hostName", "localhost");
    this.uri = props.getProperty("uri", "/myws/EPS");

    this.port = 8181;
    String configPort = props.getProperty("port");
    if(configPort != null){
        try{
            this.port = Integer.parseInt(configPort);
        }catch(NumberFormatException nfe){
            log.warning("Illegal port number: " + configPort + ", using default port (8181) instead");
        }
    }
}

@Override
public String getJAASContext() {
    return jaasCtxName;
}

public Enumeration getGroupNames(String string) throws InvalidOperationException, NoSuchUserException {
    List groupNames = new LinkedList();
    return (Enumeration) groupNames;
}

public String getAuthType() {
    return "My Webservice Realm";
}

public String getHostName() {
    return hostName;
}

public int getPort() {
    return port;
}

public String getUri() {
    return uri;
}
}

А затем реализация LoginModule:

public class WebserviceLoginModule extends AppservPasswordLoginModule {

// all variables starting with _ are supplied by the superclass, and must be filled
// in appropriately

@Override
protected void authenticateUser() throws LoginException {
    if (_username == null || _password == null) {
        throw new LoginException("username and password cannot be null");
    }

    String[] groups = this.getWebserviceClient().login(_username, _password);

    // must be called as last operation of the login method
    this.commitUserAuthentication(groups);
}

@Override
public boolean commit() throws LoginException {
    if (!_succeeded) {
        return false;
    }

    // fetch some more information through the webservice...

    return super.commit();
}

private WebserviceClient getWebserviceClient(){
    return theWebserviceClient;
}
}

наконец, в Царстве должен быть привязан к LoginModule. Это делается на уровне файла конфигурации JAAS, который в glassfish v2 находится в вашем домене /config/login.conf. Добавьте следующие строки в конец этого файла:

myWebserviceRealm { // use whatever String is returned from you realm's getJAASContext() method
    my.auth.login.WebserviceLoginModule required;
};

Это то, что заставило меня работать на Glassfish. Опять же, это решение не переносимо между серверами приложений, но, насколько я могу судить, не существует существующего переносимого решения.

Существует ли какой-либо стандартный или широко поддерживаемый способ реализации пользовательского Царства? Возможно ли каким-либо образом развернуть эту область из.war, или ее всегда нужно загружать из собственного пути к классу сервера?

Существует абсолютно стандартный способ реализации пользовательского Realm или, в более общем смысле, пользовательский модуль аутентификации. Это можно сделать с помощью JASPIC / JASPI / JSR 196 SPI / API. JASPIC является стандартной частью любой полной реализации Java EE 6, но, к сожалению, не является частью веб-профиля Java EE 6.

Однако, несмотря на то, что JASPIC является частью Java EE 6, он не оптимально поддерживается поставщиками. GlassFish и WebLogic, кажется, имеют очень хорошие реализации, JBoss AS и Geronimo немного более проблематичны. Ведущий инженер JBoss по этой теме (Анил Салдхана) даже заявил, что на данный момент он отказывается активировать JASPIC по умолчанию. Некоторые из наиболее серьезных ошибок в Jboss AS 7.1 были недавно исправлены, но, поскольку больше не запланировано публичных выпусков JBoss 7.1.x, а JBoss AS 7.2 все еще не так давно, это означает, что на данный момент, по крайней мере, на JBoss JASPIC хлопотно.

Еще одна неприятная проблема заключается в том, что сам модуль аутентификации может быть стандартизирован, но не существует декларативного способа (читать файл XML) для его стандартизации.

Возможно ли каким-либо образом развернуть эту область из.war, или ее всегда нужно загружать из собственного пути к классу сервера?

С JASPIC модуль аутентификации ('realm') действительно может быть загружен из.war. Я не уверен на 100%, гарантируется ли это спецификацией, но из 4 протестированных мной серверов (GlassFish, WebLogic, Geronimo и JBoss AS) они все это поддержали. Geronimo, к сожалению, имеет некоторое состояние гонки в своей программной регистрации, так что вам нужен уродливый обходной путь, выполнив горячее развертывание дважды, но в конце концов он загрузит модуль из.war.

Что касается запатентованных механизмов, то, по крайней мере, JBoss AS всегда поддерживал загрузку модуля (например, подкласс org.jboss.security.auth.spi.AbstractServerLoginModule) из.war или.ear.

Недавно я написал сообщение в блоге на эту тему, в котором есть еще несколько деталей.

Вы никогда не можете развернуть область из WAR, потому что область НЕ является Артефактом Приложения, это - Артефакт Контейнера (таким образом, фраза "безопасность на основе контейнера"). Вы можете настроить свое приложение на использование определенной области, предоставляемой контейнером, но приложение не может предоставить это само.

Тем не менее, хотя все контейнеры различны и эти области НЕ переносимы, простой здравый смысл уменьшит различия до небольшого количества склеенного кода, необходимого для интеграции с контейнером, если вы ищете мобильность.

Изучив документацию Suns, вы увидите, что вам нужно написать собственный LoginModule, который расширяет класс, специфичный для их сервера приложений. Это кажется немного отсталым для меня и недостатком Glassfish.

Если вы хотите сделать это более переносимым, я бы посоветовал поместить большую часть реализации в пользовательский LoginModule, разработанный для стандартных интерфейсов JavaEE, а затем иметь тонкий уровень реализации, специфичный для Glassfish, который делегирует стандартной переносимой реализации.,

Проверьте статью Sun на эту тему.

Я никогда не делал этого сам, но я уверен, что каждая AS дает вам возможность зарегистрировать новые области (домены безопасности).

Вероятно, он не будет на 100% переносимым, и для каждой AS вам может потребоваться отдельный конфигурационный XML, но в принципе нет причин, по которым код должен быть другим.

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