Reputation: 13
I would implement a security layer for java RMI, with dynamic proxy mechanism. I've some class with remote interface that bind in rmi registry, now I'm coding a class SecurityInvocationHandler, code below:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
/**
*
* @author andrew
* @param <T>
*/
public class SecurityInvocationHandler<T> extends SuperRemoteInterface implements InvocationHandler {
final T remoteInterface;
public static <T> T newInstance(final T obj, RMIClientSocketFactory rcsf, RMIServerSocketFactory rssf) throws RemoteException {
return (T) java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new SecurityInvocationHandler(obj, rcsf, rssf));
}
private SecurityInvocationHandler(T remoteInterface, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException {
super(csf, ssf);
this.remoteInterface = remoteInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Invoke method -> " + method.getName());
//TODO
return method.invoke(remoteInterface, args);
}
}
SuperRemoteInterface is parent of all classes with Interface "Remote":
import java.rmi.RemoteException;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import Config.SysConfiguration;
import java.rmi.server.UnicastRemoteObject;
public class SuperRemoteInterface extends UnicastRemoteObject {
protected SysConfiguration conf;
protected SuperRemoteInterface() throws RemoteException {
super();
}
protected SuperRemoteInterface(RMIClientSocketFactory clientFactory, RMIServerSocketFactory serverFactory) throws RemoteException {
super(0, clientFactory, serverFactory);
}
}
In the main of Server RMI I proxy Object and bind it in rmiregistry:
import /****/
public class ServerRMI extends UnicastRemoteObject {
public ServerRMI() throws RemoteException {
}
/*...*/
public static void main(String[] args) {
/*.....*/
try {
//Registry r = LocateRegistry.getRegistry();
Registry r = LocateRegistry.createRegistry(port);
RMIClientSocketFactory clientFactory = new RMISSLClientSocketFactory();
RMIServerSocketFactory serverFactory = new RMISSLServerSocketFactory();
AInterface proxy = (AInterface)SecurityInvocationHandler.newInstance(new AObject(conf), clientFactory, serverFactory);
r.bind("AObject", proxy);
/* ..... */
} catch (Exception e) {
//e.printStackTrace();
System.exit(-1);
}
}
}
Binding it's ok, but in the client side when lookup "AObject", I have this error:
java.lang.ClassCastException: cannot assign instance of $Proxy80 to field java.lang.reflect.Proxy.h of type java.lang.reflect.InvocationHandler in instance of $Proxy79
at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2039)
at java.io.ObjectStreamClass.setObjFieldValues(ObjectStreamClass.java:1212)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1952)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1870)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
at java.rmi.Naming.lookup(Naming.java:84)
at login_web.GetRemoteInterface.getAInterface(GetRemoteInterface.java:35)
.....
Client code is:
public class GetRemoteInterface {
private static final String _port = ":nnnn";
private String hostAddress;
public GetRemoteInterface() throws UnknownHostException {
/*....*/
public AInterface getAInterface() throws MalformedURLException, RemoteException, NotBoundException{
return (AInterface) Naming.lookup("//"+hostAddress+_port+"/AObject");
}
}
Without proxy mechanism lookup ok, with these codes not work. Maybe it isn't possible binding a proxed object with java rmi??
Thanks in advance.
P.S. sorry for my English
Upvotes: 1
Views: 3827
Reputation: 1653
Thanks for EJP's advice.
I have try this solution, UnicastRemoteObject.exportObject
really helps that proxy code is now run in server side but not client side.
UnicastRemoteObject.exportObject(proxy, 0)
works as expected, I do not have to modify the remote object constructor to call super() because the default super constructor is calling UnicastRemoteObject(0)
I have to wrap the invoke call to handle the exception carefully like
@Override
public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
try {
return method.invoke(remote, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
or else client side would got a java.lang.reflect.UndeclaredThrowableException
instead of the correct one.
Upvotes: 0
Reputation: 311023
The basic problem here is that you need to export the proxy object itself, not the invocation handler. Otherwise the proxy object gets serialized to the Registry, instead of its stub, with the consequences we see.
So you need to make the following adjustments:
SecureRemoteInvocationHandler
doesn't need to extend UnicastRemoteObject
either directly or indirectly.Remote proxyStub = UnicastRemoteObject.exportObject(proxy, 0, csf, ssf);
before r.bind()
in ServerRMI,
where csf
and ssf
are the socket factories. (I renamed them in my code.)There are other improvements you can make:
public class SecurityInvocationHandler<T extends Remote>
for better type-safety, and similarly:
public static <T extends Remote> T newInstance(...)
You need to make the variable containing the result of LocateRegistry.createRegistry()
static so it doesn't get garbage-collected.
You need to get adjust all remote object contructors to call super()
with a port number, so you get dynamic stubs.
You won't get much further than this until you sort out what is required for the SSL handshake to complete. You need to define javax.net.ssl.keyStore/keyStorePassword
in the server, and javax.net.ssl.trustStore
in the client if you aren't using the default one (i.e. if the server has a self-signed certificate).
The reason it doesn't work your way is that your exported SecurityInvocationHandler
replaces itself with its stub during serialization, and that stub isn't an InvocationHandler,
because InvocationHandler
isn't a remote interface, so when the object gets deserialized it can't be reassembled, as there is no InvocationHandler
to store in the dynamic proxy, just this stub, which the dynamic proxy doesn't know from Adam.
Upvotes: 2