سؤال

I want to write a web frontend that wants to "propagate" the HTTP authentication received from the browser to a JBoss AS 4.2.3 that exposes numerous @Remote interfaces.

Consider the following trivial simulation of RMI call concurrency:

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 uses jbossall-client 4.2.3 and goes to a JBoss AS 4.2.3.

The .whoami() method simply echoes the logged-in username. As it turns our, this results in both calls saying they are made by "user2". Presumably, the underlying connection is shared and only authenticated using the last seen properties bundle.

In short, this sucks. Some preliminary testing indicates that the same problem remains in JBoss AS 7 so no luck.

Is there any other RMI client implementation I can use or any parameter I can pass in the prop bundle to make the InitialContexts not share their login info? Alternatively, can someone point me to the code that needs to be hacked to make this possible?

UPDATE:

As per request:

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+"]"); 
}
}

Running with two workers yields:

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]

Running with 5 threads yields:

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)

Making the initial connection takes about 100 ms, so I tried the following (sleeping 10 ms between calls to get good overlap):

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]
هل كانت مفيدة؟

المحلول

From the docs for org.jboss.security.jndi.JndiLoginInitialContextFactory :

During the getInitialContext callback from the JNDI naming, layer security context identity is populated with the username ... and the credentials ... There is no actual authentication of this information. It is merely made available to the jboss transport layer for incorporation into subsequent invocations

in this case, by the time you get to invoke your beans, user2 is the last principal set and so is the one available to be used by the jboss transport layer.

However, from the jboss4 source, it looks like you can make the security context scoped to the thread context, in which case your threaded test should work, simply add this property:

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

Another solution would be using org.jboss.security.jndi.LoginInitialContextFactory instead of org.jboss.security.jndi.JndiLoginInitialContextFactory, unlike JndiLoginInitialContextFactory, LoginInitialContextFactory will try to authenticate when the look up is made, not when the EJB is invoked, you could give it a try, even though in the docs, they recommend JndiLoginInitialContextFactory when it comes to EJB authorization on remote clients

نصائح أخرى

The basic problem here is that you haven't close the first context before you use the second one in the same thread. I doubt that this is a fair test. It would be more interesting to actually make the two concurrent, by running them both in separate threads.

When the getInitialContext() is being called from the JNDI, the Security Layer invokes a wrapper with the credential tiles; which is factually never verified with a source, it is just type of a virtual representation of the tiles to JBOSS for subsequent calls to the same entity model. In your case, user2 is the last one to be available to JBOSS.


  • Alternatively, you can also use multiple instances of JBOSS on the same machine by using ServiceBindingManager. This could help you keep a track of all RMI calls you make, also the properties for the Connector Object do work because itself is a JMX Bean Object.
  • You can also use a threaded model which can give you additional security by adding a property

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

And just as a suggestion, I found online Use JndiLoginInitialContextFactory for EJB Authentication on remote clients.

Hope this helps!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top