RMI源码浅析
字数 2295 2025-08-24 07:48:09
RMI源码深度分析与安全机制详解
一、RMI核心概念与架构
1.1 RMI基本定义
Remote Method Invocation(远程方法调用)是一种机制,允许在某个Java虚拟机上的对象调用另一个Java虚拟机中的对象上的方法。关键特性包括:
- 远程调用的对象必须实现Remote接口
- 参数通过"marshalled"(序列化)从本地发送到远程虚拟机
- 结果从远程虚拟机"unmarshalled"(反序列化)后返回调用方
- 方法调用导致的异常会传递回调用方
1.2 RMI核心组件
- Registry:注册中心,管理远程对象引用
- Stub:客户端代理,负责编组请求和解析响应
- Skeleton:服务端存根,负责解析请求和编组响应
- Transport Layer:底层通信层,处理网络传输
二、服务端Registry创建过程
2.1 Registry创建入口
Registry registry = LocateRegistry.createRegistry(1099);
2.2 核心调用链
LocateRegistry.createRegistry()new RegistryImpl(port)setup(new UnicastServerRef(lref))
2.3 关键对象关系
RegistryImpl
|- UnicastServerRef
|- LiveRef
|- TCPEndpoint
|- TCPTransport
2.4 详细流程分析
-
LiveRef初始化:
- 创建ObjID(对象标识符)
- 通过TCPEndpoint.getLocalEndpoint()创建网络端点
- TCPEndpoint内部维护static Map localEndpoints缓存端点
-
TCPTransport初始化:
- 负责底层Socket通信
- 监听指定端口(默认1099)
- 使用线程池处理连接
-
setup核心操作:
- 创建RegistryImpl_Stub(客户端代理)
- 创建RegistryImpl_Skel(服务端存根)
- 将Target对象注册到ObjectTable
三、远程对象创建与绑定
3.1 远程对象要求
- 必须实现java.rmi.Remote接口
- 远程方法必须声明抛出RemoteException
- 推荐继承UnicastRemoteObject(非强制)
3.2 对象导出过程
protected UnicastRemoteObject() throws RemoteException {
this(0); // 0表示自动分配端口
exportObject((Remote)this, port);
}
3.3 代理对象创建
-
Util.createProxy()流程:
- 检查是否实现Remote接口
- 检查是否存在_Stub类
- 存在则反射创建Stub实例
- 不存在则创建动态代理
-
Stub生成规则:
- 接口名_Stub
- 必须继承RemoteStub
- 包含LiveRef引用
四、客户端调用机制
4.1 Registry获取
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
4.2 调用流程
- 创建LiveRef和UnicastRef
- 通过Util.createProxy创建RegistryImpl_Stub
- lookup触发实际网络请求
4.3 网络交互细节
-
握手过程:
- 客户端发送Magic(0x4b)和Version
- 服务端返回ProtocolAck
-
请求处理:
- 客户端通过StreamRemoteCall封装请求
- 服务端通过AcceptLoop接收连接
- ConnectionHandler处理具体请求
五、安全机制与漏洞点
5.1 反序列化漏洞点
-
客户端侧:
- StreamRemoteCall.executeCall()异常处理
- lookup()读取服务端返回对象
- UnicastRef.unmarshalValue读取返回值
-
服务端侧:
- RegistryImpl_Skel.dispatch读取操作参数
- 方法调用时的参数反序列化
5.2 安全防护
-
白名单校验:
((MarshalInputStream)in).useCodebaseOnly(); -
接口哈希验证:
if (var4 != 4905912898345647071L) { throw new SkeletonMismatchException(...); } -
访问控制:
checkAcceptPermission(acc);
六、JRMP协议分析
6.1 协议结构
- 魔数(Magic):0x4b
- 版本号(Version)
- 协议类型(Protocol):
- SingleOpProtocol
- StreamProtocol
- MultiplexProtocol
6.2 通信流程
-
客户端发送:
- 操作类型(Call/Ping/DGCAck)
- 方法参数序列化数据
-
服务端响应:
- NormalReturn/ExceptionalReturn
- 结果数据或异常对象
七、核心设计模式
7.1 代理模式
- 静态代理:_Stub/_Skel
- 动态代理:Proxy.newProxyInstance
7.2 工厂模式
- LocateRegistry作为Registry工厂
- Util负责Stub/Skel创建
7.3 责任链模式
- 调用链:Stub → UnicastRef → LiveRef → TCPTransport
- 处理链:AcceptLoop → ConnectionHandler → Dispatcher
八、性能优化策略
8.1 连接复用
- freeList缓存空闲连接
- TCPEndpoint缓存
8.2 异步处理
- AcceptLoop独立线程
- ConnectionThreadPool处理请求
8.3 懒加载
- Stub/Skel延迟创建
- 连接按需建立
九、典型问题排查
9.1 常见异常
- MarshalException:序列化失败
- UnmarshalException:反序列化失败
- SkeletonMismatchException:接口不匹配
- ConnectException:连接拒绝
9.2 调试技巧
-
启用RMI日志:
System.setProperty("sun.rmi.server.logCalls", "true"); -
网络抓包分析JRMP流量
十、最佳实践建议
-
安全实践:
- 启用SecurityManager
- 限制反序列化类
- 使用代码签名
-
性能调优:
- 合理设置连接池大小
- 优化序列化数据大小
- 考虑使用RMI-IIOP
-
设计建议:
- 接口设计粗粒度
- 避免大对象传输
- 明确异常处理策略