我将使用动态代理机制为Java RMI实现安全层。
我有一个带有绑定在rmi注册表中的远程接口的类,现在我在编写一个类SecurityInvocationHandler,代码如下:

    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是带有“ 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);
    }
}


在服务器RMI的主体中,我代理对象并将其绑定到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);
        }
    }
}


绑定是可以的,但是在客户端查找“ AObject”时,出现此错误:

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)
        .....


客户代码为:

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


}


如果没有代理机制查找,这些代码将无法正常工作。
也许不可能用Java rmi绑定代理对象?

提前致谢。

附言对不起我的英语不好

最佳答案

这里的基本问题是,您需要导出代理对象本身,而不是调用处理程序。否则,代理对象将序列化到注册表,而不是它的存根,从而导致后果。

因此,您需要进行以下调整:


SecureRemoteInvocationHandler不需要直接或间接扩展UnicastRemoteObject
您需要在Remote proxyStub = UnicastRemoteObject.exportObject(proxy, 0, csf, ssf);中的r.bind()之前添加ServerRMI,,其中csfssf是套接字工厂。 (我在代码中将其重命名。)


您还可以进行其他改进:

public class SecurityInvocationHandler<T extends Remote>


以获得更好的类型安全性,并且类似地:

public static <T extends Remote> T newInstance(...)


您需要将包含LocateRegistry.createRegistry()结果的变量设置为静态,以便不会被垃圾回收。

您需要调整所有远程对象构造函数以使用端口号调用super(),以便获得动态存根。

除非您弄清完成SSL握手所需的内容,否则您将获得更多的信息。如果未使用默认证书(即服务器具有自签名证书),则需要在服务器中定义javax.net.ssl.keyStore/keyStorePassword,在客户端中定义javax.net.ssl.trustStore

它无法按您的方式工作的原因是,导出的SecurityInvocationHandler在序列化过程中将其自身替换为存根,并且该存根不是InvocationHandler,,因为InvocationHandler不是远程接口,因此当对象获取时反序列化后,它就不能重新组合,因为在动态代理中没有要存储的InvocationHandler,只有这个存根,动态代理不知道亚当。

07-28 02:51
查看更多