RMI通过多个并发用户调用JBoss AS 4.2.3

时间:2014-02-05 13:06:55

标签: security rmi jboss-4.2.x

我想编写一个Web前端,希望将从浏览器收到的HTTP身份验证“传播”到公开大量@Remote接口的JBoss AS 4.2.3。

考虑以下对RMI调用并发的简单模拟:

Properties user1 = new Properties();
user1.setProperty(Context.INITIAL_CONTEXT_FACTORY, 
    "org.jboss.security.jndi.JndiLoginInitialContextFactory");
user1.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming");
user1.setProperty(Context.PROVIDER_URL, "127.0.0.1:1099");
user1.setProperty(Context.SECURITY_PRINCIPAL, "user1");
user1.setProperty(Context.SECURITY_CREDENTIALS, "pass1");

Properties user2 = new Properties();
user2.setProperty(Context.INITIAL_CONTEXT_FACTORY,
   "org.jboss.security.jndi.JndiLoginInitialContextFactory");
user2.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming");
user2.setProperty(Context.PROVIDER_URL, "127.0.0.1:1099");
user2.setProperty(Context.SECURITY_PRINCIPAL, "user2");
user2.setProperty(Context.SECURITY_CREDENTIALS, "pass2");

InitialContext ctx1 = new InitialContext(user1);
Mine bean1 = (Mine) ctx1.lookup("myear/MyBean/remote");
InitialContext ctx2 = new InitialContext(user2);
Mine bean2 = (Mine) ctx2.lookup("myear/MyBean/remote");

System.out.println(bean1.whoami());
System.out.println(bean2.whoami());

Call使用jbossall-client 4.2.3并转到JBoss AS 4.2.3。

.whoami()方法只是回显登录的用户名。当它转向我们时,这会导致两个调用都说它们是由“user2”构成的。据推测,底层连接是共享的,只能使用上次看到的属性包进行身份验证。

简而言之,这很糟糕。一些初步测试表明JBoss AS 7中存在同样的问题,所以没有运气。

我可以使用任何其他RMI客户端实现或者我可以在prop包中传递的任何参数使InitialContexts不共享他们的登录信息吗?或者,有人可以指向我的代码需要被黑客攻击才能实现这一目标?

更新

根据要求:

public class Worker extends Thread {
private final String pass, user;
private int correct = 0;

public Worker(String user, String pass) { this.user = user; this.pass = pass; }

public void run() {
    Properties props = new Properties();
    props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
            "org.jboss.security.jndi.JndiLoginInitialContextFactory");
    props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming");
    props.setProperty(Context.PROVIDER_URL, "127.0.0.1:1099");
    props.setProperty(Context.SECURITY_PRINCIPAL, this.user);
    props.setProperty(Context.SECURITY_CREDENTIALS, this.pass);

    try {
        InitialContext ctx = new InitialContext(props);
        for(int i = 0; i < 100; i++) {
            Mine bean = (Mine) ctx.lookup("myear/MyBean/remote");
            if(bean.whoami().equals(this.user)) this.correct++;
            Thread.sleep(2); }
        ctx.close();
    } catch (Exception e) { throw new RuntimeException(e); }
    System.out.println("Done [id="+this.getId()+", good="+this.correct+"]"); 
}
}

与两名工人一起运行产生:

public static void main(String[] args) throws Exception {
    new Worker("user1", "pass1").start();
    new Worker("user2", "pass2").start();
}

Done [t=9, good=0]
Done [t=10, good=100]

使用5个线程运行产生:

public static void main(String[] args) throws Exception {
    new Worker("user1", "pass1").start();
    new Worker("user2", "pass2").start();
    new Worker("user3", "pass3").start();
    new Worker("user4", "pass4").start();
    new Worker("user5", "pass5").start(); 
}

Caused by: javax.ejb.EJBAccessException: Authentication failure
at org.jboss.ejb3.security.Ejb3AuthenticationInterceptor.handleGeneralSecurityException(Ejb3AuthenticationInterceptor.java:68)
at org.jboss.aspects.security.AuthenticationInterceptor.invoke(AuthenticationInterceptor.java:70)
at org.jboss.ejb3.security.Ejb3AuthenticationInterceptor.invoke(Ejb3AuthenticationInterceptor.java:110)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:46)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:304)
at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:106)
at org.jboss.aspects.remoting.AOPRemotingInvocationHandler.invoke(AOPRemotingInvocationHandler.java:82)
at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:809)
at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:608)
at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:406)
at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:173)
at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:163)
at org.jboss.remoting.Client.invoke(Client.java:1634)
at org.jboss.remoting.Client.invoke(Client.java:548)
at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:62)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:67)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.security.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:53)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:74)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessRemoteProxy.invoke(StatelessRemoteProxy.java:107)
at $Proxy0.whoami(Unknown Source)
at net.windwards.Worker.run(TestRMIClient.java:31)
at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:74)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:67)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.aspects.security.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:53)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:74)
at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:101)
at org.jboss.ejb3.stateless.StatelessRemoteProxy.invoke(StatelessRemoteProxy.java:107)
at $Proxy0.whoami(Unknown Source)
at net.windwards.Worker.run(TestRMIClient.java:31)

初始连接大约需要100毫秒,所以我尝试了以下内容(在调用之间休眠10毫秒以获得良好的重叠):

public static void main(String[] args) throws Exception {
    new Worker("user1", "pass1").start();
    Thread.sleep(200);
    new Worker("user2", "pass2").start();
    Thread.sleep(200);
    new Worker("user3", "pass3").start();
    Thread.sleep(200);
    new Worker("user4", "pass4").start();
    Thread.sleep(200);
    new Worker("user5", "pass5").start();
}

Done [t=9, good=1]
Done [t=14, good=12]
Done [t=15, good=14]
Done [t=16, good=15]
Done [t=17, good=100]

3 个答案:

答案 0 :(得分:2)

来自org.jboss.security.jndi.JndiLoginInitialContextFactory的{​​{3}}:

  

在来自JNDI命名的getInitialContext回调期间,使用用户名...和凭据填充图层安全上下文标识...此信息没有实际身份验证。它仅供jboss传输层使用,以便并入后续调用

在这种情况下,当你调用bean时,user2是最后一个主要集合,jboss传输层可以使用它。

但是,从docs开始,看起来你可以将安全上下文限定为线程上下文,在这种情况下你的线程测试应该可以工作,只需添加这个属性:

userN.setProperty("jnp.multi-threaded", "true");

另一种解决方案是使用org.jboss.security.jndi.LoginInitialContextFactory而不是org.jboss.security.jndi.JndiLoginInitialContextFactory,与JndiLoginInitialContextFactory不同,LoginInitialContextFactory将尝试在查找时进行身份验证,而不是在调用EJB时​​进行身份验证你可以尝试一下,即使在jboss4 source中,他们建议在远程客户端上进行EJB授权时使用JndiLoginInitialContextFactory

答案 1 :(得分:1)

这里的基本问题是在同一个线程中使用第二个上下文之前没有关闭第一个上下文。我怀疑这是一个公平的考验。通过在不同的线程中运行它们来实际使两个并发更为有趣。

答案 2 :(得分:1)

当从JNDI调用getInitialContext()时,安全层会调用带有凭证磁贴的包装器;实际上从未用源验证,它只是JBOSS的瓦片虚拟表示的类型,用于后续调用同一实体模型。 在您的情况下,user2是JBOSS可用的最后一个。


  • 或者,你也可以使用多个JBOSS实例 使用ServiceBindingManager的同一台机器。这可以帮到你 跟踪你所做的所有RMI通话,以及通话的属性 Connector对象确实有效,因为它本身就是一个JMX Bean对象。
  • 您还可以使用线程模型,它可以为您提供额外的安全性 添加属性

    userN.setProperty(“jnp.multi-threaded”,“true”);

就像一个建议,我在远程客户端上发现了在线使用JndiLoginInitialContextFactory进行EJB身份验证。

希望这有帮助!