Java Web应用程序:使用自定义领域

时间:2009-04-05 21:14:30

标签: java-ee authentication jaas jaspic

我正在编写一个需要通过webservice登录的java Web应用程序。当然,我使用的应用程序服务器(glassfish v2)提供的领域都不能解决问题。因此,我必须自己写。但是,我写的领域实现似乎完全依赖于glassfish,不能像任何其他应用程序服务器那样使用。

是否有任何标准或广泛支持的方式来实现自定义Realm?是以任何方式从.war部署该领域,还是总是需要从服务器自己的类路径加载?

5 个答案:

答案 0 :(得分:10)

注意:下面的答案仅对Java EE 5有效。正如我在其他一个答案中提到的那样,Java EE 6确实支持这一点。因此,如果您使用的是Java EE 6,请不要阅读此答案,而应阅读其他相关答案。

从我自己的研究和对这个问题的回答中我得到的答案如下:虽然JAAS是一个标准接口,但是没有统一的方法可以在各种应用服务器中编写,部署和集成JAAS Realm + LoginModule。

Glassfish v2要求您扩展一些自己实现LoginModule或Realm的内部类。但是,您可以不自定义整个登录过程,因为LoginModule接口的许多方法都在Glassfish的超类中标记为final。自定义LoginModule和Realm类必须放在AS类路径中(而不是应用程序),并且必须手动注册域(不能从.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;
}
}

最后,在Realm中必须绑定到LoginModule。这是在JAAS配置文件级别完成的,在glassfish v2中位于yourDomain / config / login.conf。在该文件的末尾添加以下行:

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

这就是玻璃鱼让我感觉有用的东西。同样,这个解决方案不能跨应用程序服务器移植,但据我所知,没有现有的便携式解决方案。

答案 1 :(得分:7)

  

是否有任何标准或广泛支持的方式来实现自定义   领域?是否有可能从.war部署该领域,或者   是否总是需要从服务器自己的类路径加载?

绝对有一种标准方法可以实现自定义Realm,或者更一般地说是自定义身份验证模块。这可以通过JASPIC/JASPI/JSR 196 SPI/API完成。 JASPIC是任何完整Java EE 6实现的标准部分,但遗憾的是它不是Java EE 6 Web Profile的一部分。

然而,尽管JASPIC是Java EE 6的一部分,但它并没有得到供应商的最佳支持。 GlassFish和WebLogic似乎有很好的实现,JBoss AS和Geronimo有点问题。 JBoss关于这一主题的首席工程师(Anil Saldhana)甚至表示他目前正在refuses to activate JASPIC by default。 Jboss AS 7.1中的一些最严重的错误已经recently fixed,但由于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始终支持从.war或.ear加载模块(例如org.jboss.security.auth.spi.AbstractServerLoginModule的子类)。

wrote a blog post最近有关于此主题的详细信息。

答案 2 :(得分:2)

您永远不能从WAR部署领域,因为领域不是应用程序工件,它是容器工件(因此称为“基于容器的安全性”)。您可以将应用配置为使用容器提供的特定域,但应用程序无法自行提供。

尽管如此,虽然所有容器都是不同的,并且这些领域不是便携式的,但是如果您正在寻找可移植性,那么简单的常识将减少与集成容器所需的一小部分胶水代码之间的差异。

答案 3 :(得分:1)

快速浏览一下Suns文档,看起来你必须编写一个自定义的LoginModule来扩展他们的app server特定类。这似乎有点落后于我和Glassfish的限制。

如果你想让它更具可移植性,我建议把大部分实现放在一个针对标准JavaEE接口开发的自定义LoginModule中,然后有一个特定于Glassfish的精简实现层,代表标准,便携式实施。

答案 4 :(得分:-2)

查看 Sun's article on this subject

我自己从未真正做到这一点,但我非常有信心每个AS都为您提供了在其上注册新领域(安全域)的选项。

它可能不是100%可移植的,并且对于每个AS,您可能需要不同的配置XML,但基本上,没有理由使代码有任何不同。