一,jndi 简介
jndi 全称 java naming directory interface java ,即 java 命名与目录接口,本质上就是一个提供通过名称查找对应的对象或资源的接口,将底层的实现细节进行封装,使用户只需通过同一的对外接口来进行简洁操作
示例:Object obj = new InitialContext().lookup(“ldap://127.0.0.1:1389”)
通过协议名调用对应的 ldap provider 或 rmi provider 来具体实现
二,调用链
以 new InitialContext().lookup(“ldap://127.0.0.1:1389/evil”); 为入口
InitialContext.lookup() —>
根据 ldap 选择对应 URL Context
—>ladpCtx.lookup()
—>LdapCtx.c_lookup()
—>从 LDAP Server 获取 Entry/Attributes
—>Obj.decodeReference()
—>ObjectFactory.getObjectInstance()
三,关键类及机制
1.InitialContext
jndi 中的入口类,用来初始化上下文,初始化上下文时会根据系统属性或传入的参数动态加载对应的工厂类


可以看到类中有哈希表类型的 myPross 属性,用来保存工厂类映射,init 方法根据INITIAL_CONTEXT_FACTORY来创建对应的工厂
2.Reference
reference 类表示一个对象的引用描述,告诉该怎么创建相应的对象

包含了类名,工厂类名,工厂类地址以及其他参数
3.ObjecFactory
ObjectFactory ,类工厂接口 是 JNDI 用来根据 Reference 创建真实对象的接口,实现了这个接口的称为工厂类

工厂类接收一个 Reference 对象通过 getObjectInstance 方法来反射创建对应的实例并返回
四,详细调用链
1.lookup()

跟进 getUrlDefaultInitCtx(name),看这样子就是返回对应的工厂类返回的实例




持续跟进到 getUrlObject 方法中,这个方法才是真正处理名称到对应实例的方法,getURLObject 根据协议 scheme(如 “ldap”),利用 ResourceManager.getFactory 从 Context.URL_PKG_PREFIXES 和默认包前缀 com.sun.jndi.url 中依次拼接类名(格式:包前缀.scheme.schemeURLContextFactory),尝试加载并实例化对应的 ObjectFactory。然后调用该工厂的 getObjectInstance 方法,返回一个适用于该协议的 Context 对象(如 LdapCtx),后续即可通过该 Context 执行 lookup 等操作,最终获取远程对象


在InitialContext 类中的 lookup 方法处打断点,跟进到 lookup(name)发现跳转到了ldapURLContext 类中的 lookup 方法,说明我们已经进入了对应的 URL Context

LdapURLContext 负责解析 url,创建 LADP Context,将真正的查询交给 LdapCtx
真正的处理方法为 c_lookup(),向 LDAP 服务器发起查询请求,然后将获取到的条目转换为 java 对象

Obj.decodeObject() 是 LDAP Provider 解析 Java 对象属性的关键方法。
它会根据 LDAP Entry 中的属性判断对象类型。
如果存在 javaSerializedData
↓
走反序列化路径
如果 objectClass 是 javaNamingReference
↓
走 Reference 路径
如果是普通属性
↓
按普通 LDAP 对象处理