首页 > 代码库 > 实例化DirContext的时候报了AuthenticationException,错误码为49
实例化DirContext的时候报了AuthenticationException,错误码为49
[+]
这篇博文的原因
主要是因为犯了一个很愚蠢的错误,在实例化DirContext的时候报了AuthenticationException,错误码为49,我很疑惑,同样的方式通过ldapBrowser可以连接,在JNDI却不能连接了,同样尝试了Java的Apache Directory Studio插件也是不能连接,但是匿名连接却连接得上。基于这个原因便开始了探究初始化过程的分析,不过最后的结论却是——只是我的principal错了而已,我一直以为是我的credentials的问题。为什么同样用cn=Manager通过LDAPBrowser能访问,而通过JNDI或者ADS插件却不能访问呢?理由(大概)是访问的时候完整的用户dn应该都是cn=Manager,dc=maxcrc,dc=com,这个你定义在slap.conf的rootDn,而为什么ldapBrowser能够通过cn=Manager来访问呢?那是因为它有进行了一些拼接,因此我们总以为A是这样所以B应该是这样的想法有时候不一定通用,特别是当它们属于两个不同的项目的时候。虽然这样一来分析源码的意义不存在了,但是还是把分析过程贴出来和大家分享分享,过程也是比较简略,而且针对一种情况分析。
分析过程
该分析过程建立于以下基础:
evn.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");//导入提供者
evn.put(Context.PROVIDER_URL, "ldap://localhost/dc=maxcrc,dc=com");//服务器地址
evn.put(Context.SECURITY_AUTHENTICATION, "simple");//验证方式
在getInitialContext中主要操作如下:
...
//获取ldapURL
String str = paramHashtable != null ? (String)paramHashtable.get("java.naming.provider.url") : null;
...
//切割成string数组
arrayOfString = LdapURL.fromList(str);
...
//获取LdapCtx实例
11
getLdapCtxInstance如下:
{
if ((paramObject instanceof String)
www.chuangyed.com)
return getUsingURL((String)paramObject, paramHashtable);
if ((paramObject instanceof String[])) {
return getUsingURLs((String[])paramObject, paramHashtable);
因为我们是数组,所以这里调用的是第二个函数:
...
for (int i = 0; i < paramArrayOfString.length; www.zhuyingyule.cn i++) {
try {
return getUsingURL(paramArrayOfString[i], paramHashtable);
} catch ...
说实话我也不明白他为什么这么操作,它被URL切割成数组后实际上也只有一个值,因为它是根据“ ”切割的,而这里即使有多个值它也只处理一个值,讲道理我实在不明白,是为了代码健壮性?
getUsingURL这个方法名我们可以理解成通过URL获取目录服务上下文,现在进入它的方法体:
...
localObject = new LdapCtx(str1, str2, i, paramHashtable, localLdapURL.useSsl());
它主要是调用了这个方法,其中str1是DN,str2是host,i是port,paramHashtable是环境变量,不过已经经过了一些操作,useSsl返回true或false,它表示是否通过ssl验证,我们这里只是简单验证,因此为false.
LdapCtx的构造函数如下定义:
...
if ("ssl".equals(this.envprops.get("java.naming.security.protocol"))) {
this.useSsl = true;
}
我们可以看到,假如我们通过ssl验证的话,那我们给环境变量加上java.naming.security.protocol这个属性,并设值为ssl。此外在这里进行了设值操作,并且调用initEvn把所有的值设进去,initEvn如下:
http://blog.sina.com.cn/u/5080116125
private void initEnv() throws NamingException {
/* 2330 */ if (this.envprops == null)
/* */ {
/* 2332 */ setReferralMode(null, false);
/* 2333 */ return;
/* */ }
/* */
/* */
/* 2337 */ setBatchSize((String)this.envprops.get("java.naming.batchsize"));
/* */
/* */
/* 2340 */ setRefSeparator((String)this.envprops.get("java.naming.ldap.ref.separator"));www.aboyule.org
/* */
/* */
/* 2343 */ setDeleteRDN((String)this.envprops.get("java.naming.ldap.deleteRDN"));
/* */
/* */
/* 2346 */ setTypesOnly((String)this.envprops.get("java.naming.ldap.typesOnly"));
/* */
/* */
/* 2349 */ setDerefAliases((String)this.envprops.get("java.naming.ldap.derefAliases"));
/* */
/* */
/* 2352 */ setReferralLimit((String)this.envprops.get("java.naming.ldap.referral.limit"));
/* */
/* 2354 */ setBinaryAttributes((String)this.envprops.get("java.naming.ldap.attributes.binary"));
/* */
/* 2356 */ this.bindCtls = cloneControls((Control[])this.envprops.get("java.naming.ldap.control.connect"));
/* */
/* */
/* 2359 */ setReferralMode((String)this.envprops.get("java.naming.referral"), false);
/* */
/* */
/* 2362 */ setConnectTimeout((String)this.envprops.get("com.sun.jndi.ldap.connect.timeout"));
/* */
/* */
/* 2365 */ setReadTimeout((String)this.envprops.get("com.sun.jndi.ldap.read.timeout"));
/* */
/* */
/* */
/* 2369 */ setWaitForReply((String)this.envprops.get("com.sun.jndi.ldap.search.waitForReply"));
/* */
/* */
/* 2372 */ setReplyQueueSize((String)this.envprops.get("com.sun.jndi.ldap.search.replyQueueSize"));
/* */ }
1
它是判断你是不是v3版本的ldap,v3的字符编码和v2的不一样,因此进行了不同的编码转化,那么我们可以猜测,认证失败可能是因为版本的问题,等会儿如果我们还是找不到关键的问题所在的话我们应该试一下两个版本。
到这里解读结束,对于认证的解读有些简略,说实话这部分我也很混乱,大致的过程似乎是通过我们给定的账号去请求一个请求信息,然后看它怎么回应,如果成功就会回应一个ldap操作对象给我们,大概是这样吧。如果不去细究认证的细节的话,我们仅仅需要知道它尝试去认证了,之后获得一个保存结果的对象,在processReturnCode这个方法里面对这个状态码进行判断,认证成功状态码为0,我们这里认证失败,而且是用户名不正确则是抛出49的错误,如果认证失败则在这里负责把之前开启的资源关闭。其实如果我好好去看JNDI的文档或者好好细读ADS的文档大概就没这么多事了,懒人真的屎尿多。
实例化DirContext的时候报了AuthenticationException,错误码为49