我将使用动态代理机制为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是具有Interface&#34; Remote&#34;的所有类的父类:
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);
}
}
在Server RMI I代理对象的主体中,并将它绑定在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);
}
}
}
绑定它没关系,但在查找&#34; AObject&#34;时在客户端,我有这个错误:
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 ??
绑定一个代理对象提前致谢。
P.S。抱歉我的英文
答案 0 :(得分:2)
这里的基本问题是您需要导出代理对象本身,而不是调用处理程序。否则,代理对象会被序列化到注册表而不是它的存根,我们会看到后果。
所以你需要进行以下调整:
SecureRemoteInvocationHandler
不需要直接或间接延长UnicastRemoteObject
。Remote proxyStub = UnicastRemoteObject.exportObject(proxy, 0, csf, ssf);
r.bind()
之前添加ServerRMI,
,其中csf
和ssf
是套接字工厂。 (我在我的代码中将它们重命名。)您可以进行其他改进:
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
} isn&#39;远程接口,所以当对象被反序列化时,它不能被重新组装,因为没有InvocationHandler
存储在动态代理中,只有这个存根,动态代理没有#&# 39;从亚当那里得知。
答案 1 :(得分:0)
感谢EJP的建议。
我尝试过这个解决方案,UnicastRemoteObject.exportObject
确实有助于代理服务器代码现在在服务器端运行,但不能在客户端运行。
UnicastRemoteObject.exportObject(proxy, 0)
按预期工作,我不必修改远程对象构造函数来调用super(),因为默认的超级构造函数正在调用UnicastRemoteObject(0)
我必须像调用
一样包装调用调用来处理异常@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();
}
}
或者客户端会得到java.lang.reflect.UndeclaredThrowableException
而不是正确的。{/ p>