免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4868 | 回复: 1
打印 上一主题 下一主题

[ldap] 跟踪Sun JNDI LDAP Service Provider底层通讯 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-07-18 21:38 |只看该作者 |倒序浏览
跟踪Sun JNDI LDAP Service Provider底层通讯对Sun JNDI LDAP Service Provider底层与LDAP Server通讯进行跟踪,获取更多有用的debug信息。当然,很多情况下我们可以通过在服务器端,或者负载均衡服务器上使用tcpdump抓包,然后再使用类似ethereal的工具进行分析,可以获得直观清晰的数据。但是仍然有不足的地方:
1. 当用户没有服务器的适当权限,比如没有root身份不能执行tcpdump
2. 当分析工具不支持解析中文时,而数据包中又存在中文字符时 所以,通过Sun JNDI LDAP Service Provider,直接获取客户端和Server间的通讯信息,同时可以对中文进行支持,以满足特殊情况下的需要。

--
一般情况下,我们可以通过如下的设置来跟踪Sun JNDI LDAP Service Provider与LDAP Server通讯的情况:

    Hashtable p= new Hashtable();
    p.put( DirContext.PROVIDER_URL, "[url=]ldap://localhost:389[/url]");
    p.put( DirContext.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    p.put( DirContext.SECURITY_CREDENTIALS, "directory");
    p.put( DirContext.SECURITY_PRINCIPAL, "cn=Directory Manager");
    p.put( "com.sun.jndi.ldap.trace.ber", System.out);
    InitialDircontext ctx= new InitialDirContext( p);

然后我们进行LDAP相关的操作:

  public static void main( String[] args) throws Exception {
    DirContext ctx= null;
    try {
      ctx= getCtx();
      SearchControls cons= new SearchControls();
      cons.setSearchScope( 1);
      NamingEnumeration enu= ctx.search( "dc=com", "objectclass=*", cons);
      enu.close();
    }catch ( NamingException ex) {
      ex.printStackTrace();
    }finally {
      try {
        if ( ctx!= null) ctx.close();
      }catch ( Exception ex) {}
    }
  }

下面是默认的Hex格式输出:


-> localhost:389 0000: 30 29 02 01 01 60 24 02   01 03 04 14 63 6E 3D 44  0)...`$.....cn=D
0010: 69 72 65 63 74 6F 72 79   20 4D 61 6E 61 67 65 72  irectory Manager
0020: 80 09 64 69 72 65 63 74   6F 72 79                 ..directory
<- localhost:389 0000: 30 0C 02 01 01 61 07 0A   01 00 04 00 04 00        0....a........
-> localhost:389 0000: 30 4F 02 01 02 63 2D 04   0D 64 63 3D 74 61 6E 6E  0O...c-..dc=tann
0010: 69 6E 2E 63 6F 6D 0A 01   01 0A 01 03 02 01 00 02  in.com..........
0020: 01 00 01 01 00 87 0B 6F   62 6A 65 63 74 63 6C 61  .......objectcla
0030: 73 73 30 00 A0 1B 30 19   04 17 32 2E 31 36 2E 38  ss0...0...2.16.8
0040: 34 30 2E 31 2E 31 31 33   37 33 30 2E 33 2E 34 2E  40.1.113730.3.4.
0050: 32                                                 2
<- localhost:389 0000: 30 7F 02 01 02 64 7A 04   2A 63 6E 3D 44 69 72 65  0....dz.*cn=Dire
0010: 63 74 6F 72 79 20 41 64   6D 69 6E 69 73 74 72 61  ctory Administra
0020: 74 6F 72 73 2C 20 64 63   3D 74 61 6E 6E 69 6E 2E  tors, dc=tannin.
0030: 63 6F 6D 30 4C 30 28 04   0B 6F 62 6A 65 63 74 43  com0L0(..objectC
0040: 6C 61 73 73 31 19 04 03   74 6F 70 04 12 67 72 6F  lass1...top..gro
0050: 75 70 6F 66 75 6E 69 71   75 65 6E 61 6D 65 73 30  upofuniquenames0
0060: 20 04 02 63 6E 31 1A 04   18 44 69 72 65 63 74 6F   ..cn1...Directo
0070: 72 79 20 41 64 6D 69 6E   69 73 74 72 61 74 6F 72  ry Administrator
0080: 73                                                 s
-> localhost:389 0000: 30 23 02 01 03 50 01 02   A0 1B 30 19 04 17 32 2E  0#...P....0...2.
0010: 31 36 2E 38 34 30 2E 31   2E 31 31 33 37 33 30 2E  16.840.1.113730.
0020: 33 2E 34 2E 32                                     3.4.2
-> localhost:389 0000: 30 22 02 01 04 42 00 A0   1B 30 19 04 17 32 2E 31  0"...B...0...2.1
0010: 36 2E 38 34 30 2E 31 2E   31 31 33 37 33 30 2E 33  6.840.1.113730.3
0020: 2E 34 2E 32                                        .4.2
<- localhost:389 0000: 30 5B 02 01 02 64 56 04   18 6F 75 3D 47 72 6F 75  0[...dV..ou=Grou
0010: 70 73 2C 20 64 63 3D 74   61 6E 6E 69 6E 2E 63 6F  ps, dc=tannin.co
0020: 6D 30 3A 30 28 04 0B 6F   62 6A 65 63 74 43 6C 61  m0:0(..objectCla
0030: 73 73 31 19 04 03 74 6F   70 04 12 6F 72 67 61 6E  ss1...top..organ
0040: 69 7A 61 74 69 6F 6E 61   6C 75 6E 69 74 30 0E 04  izationalunit0..
0050: 02 6F 75 31 08 04 06 47   72 6F 75 70 73           .ou1...Groups
<- localhost:389 0000: 30 5B 02 01 02 64 56 04   18 6F 75 3D 50 65 6F 70  0[...dV..ou=Peop
0010: 6C 65 2C 20 64 63 3D 74   61 6E 6E 69 6E 2E 63 6F  le, dc=tannin.co
0020: 6D 30 3A 30 28 04 0B 6F   62 6A 65 63 74 43 6C 61  m0:0(..objectCla
0030: 73 73 31 19 04 03 74 6F   70 04 12 6F 72 67 61 6E  ss1...top..organ
0040: 69 7A 61 74 69 6F 6E 61   6C 75 6E 69 74 30 0E 04  izationalunit0..
0050: 02 6F 75 31 08 04 06 50   65 6F 70 6C 65           .ou1...People
<- localhost:389 0000: 30 81 9B 02 01 02 64 81   95 04 1E 6F 75 3D 53 70  0.....d....ou=Sp
0010: 65 63 69 61 6C 20 55 73   65 72 73 2C 64 63 3D 74  ecial Users,dc=t
0020: 61 6E 6E 69 6E 2E 63 6F   6D 30 73 30 28 04 0B 6F  annin.com0s0(..o
0030: 62 6A 65 63 74 43 6C 61   73 73 31 19 04 03 74 6F  bjectClass1...to
0040: 70 04 12 6F 72 67 61 6E   69 7A 61 74 69 6F 6E 61  p..organizationa
0050: 6C 55 6E 69 74 30 15 04   02 6F 75 31 0F 04 0D 53  lUnit0...ou1...S
0060: 70 65 63 69 61 6C 20 55   73 65 72 73 30 30 04 0B  pecial Users00..
0070: 64 65 73 63 72 69 70 74   69 6F 6E 31 21 04 1F 53  description1!..S
0080: 70 65 63 69 61 6C 20 41   64 6D 69 6E 69 73 74 72  pecial Administr
0090: 61 74 69 76 65 20 41 63   63 6F 75 6E 74 73        ative Accounts
<- localhost:389 0000: 30 0C 02 01 02 65 07 0A   01 00 04 00 04 00        0....e........


我们虽然取到了底层通讯的数据,但是根本无法直接阅读,因此有两种方法可以输出符合我们习惯的内容:
1. 通过反编译Sun的class文件,来获取LDAPMessage格式的输出
2. 增加一个FilterOutputStream,对Sun输出的内容进行处理,处理后输出LDAPMessage格式的内容

两种方法都可以实现,但是第二种性能很差,Sun既要花时间格式化成Hex格式,我们还要在FilterOutputStream中反其道而行之,重新恢复成原始数据,然后再解析,过程中涉及到大量的String - byte[] 操作,因此性能比较差。但是不像第一种,第一种应该是一种违法的行为。
按照我们的要求格式化后的输出如下:

-> localhost:389 LDAPMessage {
  messageID = 1,
  protocolOp = {
    bindRequest = {
      version = 3,
      name = cn=Directory Manager,
      authentication = {
        simple = directory      }
    }
  } }
<- localhost:389 LDAPMessage {
  messageID = 1,
  protocolOp = {
    bindResponse = {
      resultCode = 0,
      matchedDN = ,
      errorMessage =
    }
  } }
-> localhost:389 LDAPMessage {
  messageID = 2,
  protocolOp = {
    searchRequest = {
      baseObject = dc=tannin.com,
      scope = 1,
      derefAliases = 3,
      sizeLimit = 0,
      timeLimit = 0,
      typesOnly = false,
      filter = {
        present = objectclass      },
      attributes = {
      }
    }
  }
,
  controls = {
    {
      controlType = 2.16.840.1.113730.3.4.2,
      criticality = false
    }
  }
}
<- localhost:389 LDAPMessage {
  messageID = 2,
  protocolOp = {
    searchResEntry = {
      objectName = cn=Directory Administrators, dc=tannin.com,
      attributes = {
        {
          type = objectClass,
          vals = {
            top,
            groupofuniquenames
          }
        },
        {
          type = cn,
          vals = {
            Directory Administrators
          }
        }
      }
    }  } }
-> localhost:389 LDAPMessage {
  messageID = 3,
  protocolOp = {
    abandonRequest = 2  }
,
  controls = {
    {
      controlType = 2.16.840.1.113730.3.4.2,
      criticality = false
    }
  }
}
<- localhost:389 LDAPMessage {
  messageID = 2,
  protocolOp = {
    searchResEntry = {
      objectName = ou=Groups, dc=tannin.com,
      attributes = {
        {
          type = objectClass,
          vals = {
            organizationalunit,
            top
          }
        },
        {
          type = ou,
          vals = {
            Groups
          }
        }
      }
    }  } }
-> localhost:389 LDAPMessage {
  messageID = 4,
  protocolOp = {
    unbindRequest = NULL  }
,
  controls = {
    {
      controlType = 2.16.840.1.113730.3.4.2,
      criticality = false
    }
  }
}

  • 由于LDAP协议规定,LDAP Message是符合ASN.1 BER规则的字节流,但是我们没有必要自己来实现Ber Encoder/Decoder。我使用了很早以前IBM的Snacc for Java,它可以自动根据LDAP V3 ASN Definitions来生成Java类文件,然后使用它的Encoder/Decoder就可以处理了。由于IBM已经移除了Snacc,并且已经不再是免费的软件,所以使用上受到限制。OpenSource正有几个Project关于ASN Compiler的,但是还不够成熟,相信不久的将来就可以取代Snacc。
  • 从上面的Trace中也可以看出,Sun在处理NamingEnumeration.close()处有一个缺陷,即当对一个NamingEnumeration实例在没有完成全部遍历时,执行close()操作会导致Sun LDAP SP 向LDAP Server发送一个LDAP Abandon Request。因此我们需要在即使只有一个返回值的情况下也要完成遍历,或者干脆不关闭NamingEnumeration来避免这种情况。

论坛徽章:
0
2 [报告]
发表于 2007-07-18 21:39 |只看该作者
介绍:

跟踪JNDI底层与LDAP的交互,并以LDAP DEFINITIONS(ANS.1)格式输出,同时可以支持中文显示。

使用方法:


1. 将ldapmessage_trace.jar包加入到boot classpath中,即使用参数-Xbootclasspath/p:,比如:

java -Xbootclasspath/p:ldapmessage_trace.jar <your main class>

同时在代码中需要进行如下的设置,示例如下:
  /**
   *
   * @return
   * @throws NamingException
   */
  public static DirContext getCtx() throws NamingException {
    Hashtable p= new Hashtable();
    p.put( DirContext.PROVIDER_URL, "ldap://localhost:389");
    p.put( DirContext.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    p.put( DirContext.SECURITY_CREDENTIALS, "directory");
    p.put( DirContext.SECURITY_PRINCIPAL, "cn=Directory Manager");
    p.put( "com.sun.jndi.ldap.trace.ber", System.out);
    return new InitialDirContext( p);
  }

如果需要输出到文件中,可以自己起一个File Stream,然后设置到Context中。只要该对象是OutputStream子类就可以。

一切准备就绪,执行测试程序就可以从我们指定的输出流中输出LDAPMessage内容了。


2. 使用LdapMessageOutputStream来作为输出流,但是这种方法会导致较低的效率

使用方法非常简单,在获取DirContext的时候指定LdapMessageOutputStream作为输出流就可以了。  
/**
   *
   * @return
   * @throws NamingException
   */
  public static DirContext getCtx() throws NamingException {
    Hashtable p= new Hashtable();
    p.put( DirContext.PROVIDER_URL, "ldap://localhost:389");
    p.put( DirContext.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    p.put( DirContext.SECURITY_CREDENTIALS, "directory");
    p.put( DirContext.SECURITY_PRINCIPAL, "cn=Directory Manager");
    p.put( "com.sun.jndi.ldap.trace.ber", System.out);
    return new InitialDirContext( p);
  }



资源:

需要IBM Snacc4j的支持。Snacc已经被IBM废除掉了,相关的使用也不再免费。希望日后开源社区的几个ASN Compiler项目最终取代古老的Snacc……
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP