歡迎您光臨本站 註冊首頁

跟蹤Sun JNDI LDAP Service Provider底層通訊

←手機掃碼閱讀     火星人 @ 2014-03-04 , reply:0

跟蹤Sun JNDI LDAP Service Provider底層通訊

跟蹤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, "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);
    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來避免這種情況。
《解決方案》

介紹:

跟蹤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……

[火星人 ] 跟蹤Sun JNDI LDAP Service Provider底層通訊已經有877次圍觀

http://coctec.com/docs/service/show-post-37357.html