【问题标题】:Java Web Application: Using a custom realmJava Web 应用程序:使用自定义领域
【发布时间】:2010-10-17 17:01:59
【问题描述】:

我正在编写一个需要通过 web 服务执行登录的 java web 应用程序。当然,我正在使用的应用程序服务器(glassfish v2)提供的任何领域都不能解决问题。因此我不得不自己写。然而,我编写的领域实现似乎与 glassfish 完全绑定,不能在任何其他应用程序服务器中使用。

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

【问题讨论】:

  • 你能发布一个你的领域代码的例子吗?

标签: jakarta-ee authentication jaas jaspic


【解决方案1】:

注意:下面的答案仅对 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 实现和登录模块)。但是,即使是 tomcat 也不允许从您的 .war 部署您的自定义领域。

请注意,显示我的结果的所有应用程序服务器似乎都无法充分利用所有 JAAS 回调。它们似乎都只支持基本的用户名+密码方案。如果您需要比这更复杂的东西,那么您将需要找到一个不受 Java EE 容器管理的解决方案。

作为参考,并且因为我的问题在 cmets 中被要求,这里是我为 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;
};

这就是我在 glassfish 上工作的原因。同样,这个解决方案不能跨应用服务器移植,但据我所知,没有现成的可移植解决方案。

【讨论】:

    【解决方案2】:

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

    绝对有一种标准的方式来实现自定义领域,或者更一般的说法是自定义身份验证模块。这可以通过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,身份验证模块(“领域”)确实可以从 .war 加载。我不能 100% 确定规范是否保证了这一点,但在我测试的 4 台服务器(GlassFish、WebLogic、Geronimo 和 JBoss AS)中,它们都支持这一点。不幸的是,Geronimo 在其程序注册中存在某种竞争条件,因此您需要一个丑陋的解决方法,即进行两次热部署,但最终如果确实从 .war 加载模块。

    作为专有机制,至少 JBoss AS 一直支持从 .war 或 .ear 加载模块(例如 org.jboss.security.auth.spi.AbstractServerLoginModule 的子类)。

    我最近wrote a blog post关于这个话题有更多的细节。

    【讨论】:

    • 酷!这是 J2EE 6 中的新功能吗?因为我很确定这在 J2EE 5 中是不可能的。
    • @LordOfThePigs 首先是迂腐的,从来没有 J2EE 5 或 6。它变成了 Java EE ;) 但事实上,JASPIC 是随 Java EE 6 引入的。规范本身有着悠久的历史并且本身是在 Java EE 6 之前的某个时间发布的。作为一个独立的规范,供应商可以自愿实施它,这对于旨在为所有供应商带来身份验证模块可移植性的规范来说意义不大。
    【解决方案3】:

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

    也就是说,虽然所有容器都不同,并且这些领域不可移植,但如果您正在寻找可移植性,简单的常识将减少与容器集成所需的一点点胶水代码的差异。

    【讨论】:

    • 我知道这是一个非常古老的答案,但即使在 2009 年,各种服务器也允许实现“领域”(或服务器使用的任何名称,因为名称本身也没有标准化)进入战争或耳朵。至少 JBoss AS 从版本 5 开始就允许这样做了,甚至可能更早。
    【解决方案4】:

    快速浏览 Suns 文档后,您似乎必须编写一个自定义 LoginModule 来扩展他们的应用服务器特定类。这对我来说似乎有点倒退,并且是 Glassfish 的限制。

    如果您想让它更便携,我建议将大部分实现放在针对标准 JavaEE 接口开发的自定义 LoginModule 中,然后有一个特定于 Glassfish 的瘦实现层,委托给标准,可移植的实现。

    【讨论】:

    • 是的,我已经这样做了。不过这感觉很糟糕,所以我想知道是否还有其他方法。但似乎没有其他方法(至少不是 glassfish)
    • 是的,当我为 JBoss 写一篇文章时,我不记得有必要做任何如此骇人听闻的事情。抱歉,我帮不上忙
    【解决方案5】:

    查看Sun's article on this subject

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

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

    【讨论】:

      猜你喜欢
      • 2014-02-11
      • 2011-12-01
      • 2012-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-30
      • 2019-07-19
      相关资源
      最近更新 更多