简体   繁体   English

Java RMI中远程Object的绑定代理

[英]Binding proxy for remote Object in java RMI

I would implement a security layer for java RMI, with dynamic proxy mechanism. 我将使用动态代理机制为java RMI实现一个安全层。 I've some class with remote interface that bind in rmi registry, now I'm coding a class SecurityInvocationHandler, code below: 我有一些带有远程接口的类,它绑定在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 is parent of all classes with Interface "Remote": 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);      
    }
}

In the main of Server RMI I proxy Object and bind it in rmiregistry: 在服务器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);
        }
    }
}

Binding it's ok, but in the client side when lookup "AObject", I have this error: 绑定它没关系,但在查找“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)
        .....

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?? 也许不可能用java rmi绑定一个代理对象?

Thanks in advance. 提前致谢。

PS sorry for my English 对不起我的英语

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: 所以你需要做出以下调整:

  1. SecureRemoteInvocationHandler doesn't need to extend UnicastRemoteObject either directly or indirectly. SecureRemoteInvocationHandler不需要直接或间接扩展UnicastRemoteObject
  2. You need to add Remote proxyStub = UnicastRemoteObject.exportObject(proxy, 0, csf, ssf); 您需要添加Remote proxyStub = UnicastRemoteObject.exportObject(proxy, 0, csf, ssf); before r.bind() in ServerRMI, where csf and ssf are the socket factories. r.bind()ServerRMI,其中csfssf是套接字工厂。 (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. 您需要使包含LocateRegistry.createRegistry()静态结果的变量生成,因此不会对其进行垃圾回收。

You need to get adjust all remote object contructors to call super() with a port number, so you get dynamic stubs. 您需要调整所有远程对象构造函数以使用端口号调用super() ,以便获得动态存根。

You won't get much further than this until you sort out what is required for the SSL handshake to complete. 在完成SSL握手所需的操作之前,您将不会得到更多。 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 (ie if the server has a self-signed certificate). 您需要定义javax.net.ssl.keyStore/keyStorePassword在服务器和javax.net.ssl.trustStore ,如果你不使用默认的客户端(即,如果服务器具有自签名证书)。

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. 它不起作用的原因是导出的SecurityInvocationHandler在序列化期间将其自身替换为其存根,并且该存根不是InvocationHandler,因为InvocationHandler不是远程接口,所以当对象被反序列化时它不能重新组装,因为没有InvocationHandler存储在动态代理中,只有这个存根,动态代理不知道来自Adam。

Thanks for EJP's advice. 感谢EJP的建议。

I have try this solution, UnicastRemoteObject.exportObject really helps that proxy code is now run in server side but not client side. 我试过这个解决方案, UnicastRemoteObject.exportObject确实有助于代理代码现在在服务器端而不是客户端运行。

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) UnicastRemoteObject.exportObject(proxy, 0)按预期工作,我不必修改远程对象构造函数来调用super(),因为默认的超级构造函数正在调用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. 或者客户端会得到java.lang.reflect.UndeclaredThrowableException而不是正确的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM