LDAP 技术总结(2)

来源:互联网 发布:2017网络灰色项目 编辑:程序博客网 时间:2024/05/17 23:19

    /** 
      * 在当前的连接DirContext 删除 指定Context 下的 一个 / 多个属性 
      * @param context 连接后的DirContext 
      * @param cn 指定Context的名称 
      * @param attList 包含要删除的属性的名称,为List类型 
      * @throws BaseException 
      * @throws NamingException 
      */ 
     public static void deleteAttributes(DirContext context, String cn, 
                                         List attList) throws BaseException, 
             NamingException { 
         // 参数为空 
         if (context == null) { 
             String[] args = { 
                             "context"}; 
             // 打印错误日志 
             StringBuffer msglog = new StringBuffer( 
                     "empty invoke parameter context NULL "); 
             log.error(msglog.toString()); 
             throw new BaseException("error.common.parameter.empty", args); 
         } 

         // 参数为空 
         if (attList == null) { 
             String[] args = { 
                             "attList"}; 
             // 打印错误日志 
             StringBuffer msglog = new StringBuffer( 
                     "empty invoke parameter attList NULL "); 
             log.error(msglog.toString()); 
             throw new BaseException("error.common.parameter.empty", args); 
         } 
         // 参数为空 
         if (StringUtils.isEmpty(cn)) { 
             String[] args = { 
                             "cn"}; 
             // 打印错误日志 
             StringBuffer msglog = new StringBuffer( 
                     "empty invoke parameter cn NULL "); 
             log.error(msglog.toString()); 
             throw new BaseException("error.common.parameter.empty", args); 
         }

         // 为空,退出 
         if (attList.isEmpty()) { 
             return; 
         }

         Attributes attrs = new BasicAttributes();

         for (int i = 0; i < attList.size(); i++) { 
             Attribute att = null; 
             att = new BasicAttribute((String) attList.get(i)); 
             // 加入 
             attrs.put(att); 
         } 
         context.modifyAttributes(cn, DirContext.REMOVE_ATTRIBUTE, attrs); 
         // context.close(); 
     } 
    } 
封装JNDI操作LDAP服务器的工具类(3) LdapOperUtils类的其余方法

/** 
      * 在当前连接的DirContext 修改指定Context下的一个 或 多个属性 
      * @param context 连接的DirContext 
      * @param cn 指定Context下的名字 
      * @param attMap 包含List key为属性名称,当属性为多值时 
      * value 为包含多值的List,为单值时,为包含单值的String类型 
      * @throws BaseException 
      * @throws NamingException 
      */ 
     public static void modifyAttributes(DirContext context, String cn, 
                                         Map attMap) throws 
             BaseException, NamingException {

         // 参数为空 
         if (context == null) { 
             String[] args = { 
                             "context"}; 
             // 打印错误日志 
             StringBuffer msglog = new StringBuffer( 
                     "empty invoke parameter context NULL "); 
             log.error(msglog.toString()); 
             throw new BaseException("error.common.parameter.empty", args); 
         }

         // 参数为空 
         if (attMap == null) { 
             String[] args = { 
                             "attMap"}; 
             // 打印错误日志 
             StringBuffer msglog = new StringBuffer( 
                     "empty invoke parameter attMap NULL "); 
             log.error(msglog.toString()); 
             throw new BaseException("error.common.parameter.empty", args); 
         } 
         // 参数为空 
         if (StringUtils.isEmpty(cn)) { 
             String[] args = { 
                             "cn"}; 
             // 打印错误日志 
             StringBuffer msglog = new StringBuffer( 
                     "empty invoke parameter cn NULL "); 
             log.error(msglog.toString()); 
             throw new BaseException("error.common.parameter.empty", args); 
         }

         // 为空,退出 
         if (attMap.isEmpty()) { 
             return; 
         } 
         // 取所有的属性key 
         Set keySet = attMap.keySet(); 
         Iterator keyIterator = keySet.iterator(); 
         Attributes attrs = new BasicAttributes(); 
         // 迭代所有的属性key 
         while (keyIterator.hasNext()) { 
             // 取下一个属笥 
             String key = (String) keyIterator.next(); 
             Attribute att = null; 
             Object valueObj = attMap.get(key);

             if (valueObj instanceof List) { 
                 // 为List ,为多值属性 
                 att = new BasicAttribute(key); 
                 List valueList = (List) valueObj; 
                 // 加入多值属性 
                 for (int i = 0; i < valueList.size(); i++) { 
                     att.add(valueList.get(i)); 
                 } 
             } else if (valueObj instanceof String) { 
                 att = new BasicAttribute(key, valueObj); 
             } 
             // 加入 
             attrs.put(att); 
         } 
         context.modifyAttributes(cn, DirContext.REPLACE_ATTRIBUTE, attrs); 
         // context.close(); 
     }

     // 
     /** 
      * 获取连接的DirContext中指定Context下的指定属性 
      * @param context 连接的DirContext 
      * @param cn  指定Context的名称 
      * @param attNameList 要取的属性的名称List 
      * @return Map包含List ,key 为属性的名称,当属性值为多值时,Value为List类型, 
      * 否则,value 为String 类型 
      * @throws NamingException 
      */ 
     public static Map getAttributes(DirContext context, String cn, 
                                     List attNameList) throws NamingException { 
         Map attsMap = new HashMap(); 
         Attributes results = null; 
         List attValList = null; 
         String attrId = null;

         if (attNameList == null) { 
             results = context.getAttributes(cn); 
         } else { 
             if (!attNameList.isEmpty()) { 
                 // results = context.getAttributes(cn); 
                 String[] stTemp = new String[attNameList.size()]; 
/////////////////////////////////////////// 以下方法性能太低 //////////////////////////////// 
//                for (int i = 0; i < attNameList.size(); i++) { 
//                    stTemp[i] = (String) attNameList.get(i); 
//                } 
//                results = context.getAttributes(cn, 
//                                                stTemp); 
/////////////////////////////////////////////////////////////////////////////////////////// 
                 // 比较高性能的List 转为 数组的方法 
                 results = context.getAttributes(cn, 
                                                 (String[]) (attNameList.toArray(stTemp))); 
             } 
         } 
         for (int i = 0; i < attNameList.size(); i++) { 
             Attribute attr = results.get((String) attNameList.get(i)); 
             attrId = (String) attNameList.get(i); 
             if (attr != null) { 
                 if (attr.size() > 0) { 
                     NamingEnumeration vals = attr.getAll(); 
                     if (vals == null) { 
                         continue; 
                     } 
                     Object obj1 = vals.nextElement(); 
                     if (obj1 == null) { 
                         continue; 
                     } 
                     // 迭代这个属性的所有属性值 
                     while (vals.hasMoreElements()) { 
                         if (attValList == null) { 
                             attValList = new ArrayList(); 
                             attValList.add(obj1); 
                         } 
                         attValList.add(vals.nextElement()); 
                     } 
                     // 当属性为单值域时,存为字符串 
                     // 当属性为多值域时,存为包含多值域的List 
                     if (attValList != null) { 
                         attsMap.put(attrId, attValList); 
                         // 清空 
                         attValList = null; 
                     } else { 
                         attsMap.put(attrId, obj1); 
                     } 
                 } 
             } 
         } 
         // context.close(); 
         return attsMap; 
     }

     /** 
      * 在当前连接的DirContext 获取指定Context下的指定属性名称的所有属性值(一个或多个值) 
      * @param context 连接的DirContext 
      * @param cn  指定Context的cn名 
      * @param attName 属性名称 
      * @return 返回包括属性值的List 注意,当属性只有一个值时,返回的List长度为1,当属性 
      * 是多值属性时,返回List长度为属性值的数目 
      * @throws NamingException 
      */ 
     public static List getAttributeValues(DirContext context, String cn, 
                                           String attName) throws 
             NamingException { 
         List attValList = new ArrayList(); 
         List attNameList = new ArrayList(); 
         attNameList.add(attName); 
         Map attMap = null; 
         attMap = getAttributes(context, cn, attNameList);

         if (attMap != null) { 
             Object attValObj = attMap.get(attName); 
             if (attValObj instanceof String) { 
                 attValList.add((String) attValObj); 
             } else if (attValObj instanceof List) { 
                 attValList = ((List) attValObj); 
             } 
         } 
         // context.close(); 
         return attValList; 
     }

     /** 
      * 获取角色的相关信息 
      * @param context DirContext 
      * @param cn String 
      * @param attName String 
      * @return String 
      * @throws NamingException 
      */ 
     public static String getRoleAttributeValues(DirContext context, String cn, 
                                           String attName) throws 
             NamingException { 
         String result = ""; 
         List attNameList = new ArrayList(); 
         attNameList.add(attName); 
         Map attMap = null; 
         attMap = getAttributes(context, cn, attNameList);

         if (attMap != null) { 
             Object attValObj = attMap.get(attName); 
             result = (String)attValObj; 
         } 
         return result; 
     }

     /** 
      * 根据条件查找指定CN的Context下的一层所有属性 
      * @param context 连接了的DirContext 
      * @param cn 要查询的BaseCN名称 
      * @param filter 要查询的过滤字符串 
      * @return 符合查询结果的List 
      * @throws NamingException 
      */ 
     public static List searchContextOne(DirContext context, String cn, 
                                         String filter) throws 
             NamingException { 
         List resultList = new ArrayList(); 
         Map resultRowMap = null; 
         List attValList = null; 
         String attValStr = null; 
         // 实例化一个搜索器 
         SearchControls constraints = new SearchControls(); 
         // 设置搜索器的搜索范围 
         constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE); 
         // 在基目录中搜索条件为Env.MY_FILTER的所有属性 注意:这里返回是的所有的条目集合 
         NamingEnumeration results 
                 = context.search(cn, filter, constraints);

         // 打印条目的识别名(DN)及其所有的属性名,值 
         while (results != null && results.hasMore()) { 
             // 取一个条目 
             SearchResult si = (SearchResult) results.next();

             // 获取条目的所有属性集合 
             Attributes attrs = si.getAttributes(); 
             if (attrs != null) { 
                 String attrId = null; 
                 // 一行数据 
                 resultRowMap = new HashMap(); 
                 // 打印所有属性 
                 for (NamingEnumeration ae = attrs.getAll(); 
                                             ae.hasMoreElements(); ) { 
                     // 获取一个属性 
                     Attribute attr = (Attribute) ae.next(); 
                     attrId = attr.getID(); 
                     Enumeration vals = attr.getAll(); 
                     if (vals == null) { 
                         continue; 
                     } 
                     Object obj1 = vals.nextElement(); 
                     if (obj1 == null) { 
                         continue; 
                     } 
                     // 迭代这个属性的所有属性值 
                     while (vals.hasMoreElements()) { 
                         if (attValList == null) { 
                             attValList = new ArrayList(); 
                             attValList.add(obj1); 
                         } 
                         attValList.add(vals.nextElement()); 
                     } 
                     // 当属性为单值域时,存为字符串 
                     // 当属性为多值域时,存为包含多值域的List 
                     if (attValList != null) { 
                         resultRowMap.put(attrId, attValList); 
                         // 清空 
                         attValList = null; 
                     } else { 
                         resultRowMap.put(attrId, obj1); 
                     }

                 } 
             } 
             resultList.add(resultRowMap); 
         } 
         return resultList; 
     }

     /** 
      * 根所条件查找指定CN的Context下的子树下的所有属性 
      * @param context 连接了的DirContext 
      * @param cn 要查询的BaseCN名称 
      * @param filter 要查询的过滤字符串 
      * @return 符合查询结果的List 
      * @throws NamingException 
      */ 
     public static List searchContextSub(DirContext context, String cn, 
                                         String filter) throws 
             NamingException { 
         List resultList = new ArrayList(); 
         Map resultRowMap = null; 
         List attValList = null; 
         // 实例化一个搜索器 
         SearchControls constraints = new SearchControls(); 
         // 设置搜索器的搜索范围 
         constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); 
         // 在基目录中搜索条件为Env.MY_FILTER的所有属性 注意:这里返回是的所有的条目集合 
         NamingEnumeration results 
                 = context.search(cn, filter, constraints);

         // 打印条目的识别名(DN)及其所有的属性名,值 
         while (results != null && results.hasMore()) { 
             // 取一个条目 
             SearchResult si = (SearchResult) results.next();

             // 获取条目的所有属性集合 
             Attributes attrs = si.getAttributes(); 
             if (attrs != null) { 
                 String attrId = null; 
                 // 一行数据 
                 resultRowMap = new HashMap(); 
                 // 打印所有属性值 
                 for (NamingEnumeration ae = attrs.getAll(); 
                                             ae.hasMoreElements(); ) { 
                     // 获取一个属性 
                     Attribute attr = (Attribute) ae.next(); 
                     attrId = attr.getID(); 
                     Enumeration vals = attr.getAll(); 
                     if (vals == null) { 
                         continue; 
                     } 
                     Object obj1 = vals.nextElement(); 
                     if (obj1 == null) { 
                         continue; 
                     } 
                     // 迭代这个属性的所有属性值 
                     while (vals.hasMoreElements()) { 
                         if (attValList == null) { 
                             attValList = new ArrayList(); 
                             attValList.add(obj1); 
                         } 
                         attValList.add(vals.nextElement()); 
                     } 
                     // 当属性为单值域时,存为字符串 
                     // 当属性为多值域时,存为包含多值域的List 
                     if (attValList != null) { 
                         resultRowMap.put(attrId, attValList); 
                         // 清空 
                         attValList = null; 
                     } else { 
                         resultRowMap.put(attrId, obj1); 
                     } 
                 } 
             } 
             resultList.add(resultRowMap); 
         } 
         return resultList; 
     }

     /** 
      * 查找指定CN的Context下的子树下的指定属性 
      * @param context DirContext 
      * @param cn String 
      * @param filter String 
      * @param returnedAtts String[] 属性名字数组 
      * @return List 
      * @throws NamingException 
      */ 
     public static List searchContextSub(DirContext context, String cn, 
                                         String filter, String[] returnedAtts) throws 
             NamingException { 
         List resultList = new ArrayList(); 
         String attrId = null; 
         List attValList = null; 
         Map resultRowMap = null; 
         // 实例化一个搜索器 
         SearchControls constraints = new SearchControls(); 
         // 设置搜索器的搜索范围 
         constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); 
         // String[] returnedAtts = {"uniquemember"}; 
         constraints.setReturningAttributes(returnedAtts); 
         // 条目 
         NamingEnumeration results 
                 = context.search(cn, filter, constraints);

         // 迭代所有的条目 
         while (results != null && results.hasMore()) { 
             // 取一个条目 
             SearchResult si = (SearchResult) results.next(); 
             resultRowMap = new HashMap(); 
             // 获取条目的指定返回的属性 
             Attributes attrs = si.getAttributes(); 
             if (attrs != null) { 
                 // 迭代所有属性值 
                 for (NamingEnumeration ae = attrs.getAll(); 
                                             ae.hasMoreElements(); ) {

                     // 获取一个属性 
                     Attribute attr = (Attribute) ae.next(); 
                     attrId = attr.getID(); 
                     Enumeration vals = attr.getAll(); 
                     if (vals == null) { 
                         continue; 
                     } 
                     // 迭代这个属性的所有属性值 
                     while (vals.hasMoreElements()) { 
                         if (attValList == null) { 
                             attValList = new ArrayList(); 
                         } 
                         attValList.add(vals.nextElement()); 
                     } 
                     // 当属性为单值域时,存为字符串 
                     // 当属性为多值域时,存为包含多值域的List 
                     if (attValList != null) { 
                         resultRowMap.put(attrId, attValList); 
                         // 清空 
                         attValList = null; 
                     } 
                 } 
             } 
             resultList.add(resultRowMap); 
         } 
         return resultList; 
     }

     /** 
      * 查找指定CN的Context下的一层指定属性 
      * @param context DirContext 
      * @param cn String 
      * @param filter String 
      * @param returnedAtts String[] 属性名字数组 
      * @return List 
      * @throws NamingException 
      */ 
     public static List searchContextOne(DirContext context, String cn, 
                                         String filter, String[] returnedAtts) throws 
             NamingException { 
         List resultList = new ArrayList(); 
         String attrId = null; 
         List attValList = null; 
         Map resultRowMap = null; 
         // 实例化一个搜索器 
         SearchControls constraints = new SearchControls(); 
         // 设置搜索器的搜索范围 
         constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE); 
         // String[] returnedAtts = {"uniquemember"}; 
         constraints.setReturningAttributes(returnedAtts); 
         // 条目 
         NamingEnumeration results 
                 = context.search(cn, filter, constraints);

         // 迭代所有的条目 
         while (results != null && results.hasMore()) { 
             // 取一个条目 
             SearchResult si = (SearchResult) results.next(); 
             resultRowMap = new HashMap(); 
             // 获取条目的指定返回的属性 
             Attributes attrs = si.getAttributes(); 
             if (attrs != null) { 
                 // 迭代所有属性值 
                 for (NamingEnumeration ae = attrs.getAll(); 
                                             ae.hasMoreElements(); ) {

                     // 获取一个属性 
                     Attribute attr = (Attribute) ae.next(); 
                     attrId = attr.getID(); 
                     Enumeration vals = attr.getAll(); 
                     if (vals == null) { 
                         continue; 
                     } 
                     Object obj1 = vals.nextElement(); 
                     if (obj1 == null) { 
                         continue; 
                     } 
                     // 迭代这个属性的所有属性值 
                     while (vals.hasMoreElements()) { 
                         if (attValList == null) { 
                             attValList = new ArrayList(); 
                             attValList.add(obj1); 
                         } 
                         attValList.add(vals.nextElement()); 
                     } 
                     // 当属性为单值域时,存为字符串 
                     // 当属性为多值域时,存为包含多值域的List 
                     if (attValList != null) { 
                         resultRowMap.put(attrId, attValList); 
                         // 清空 
                         attValList = null; 
                     } else { 
                         resultRowMap.put(attrId, obj1); 
                     } 
                 } 
             } 
             resultList.add(resultRowMap); 
         } 
         return resultList; 
     }

     /** 
         * 在当前的连接DirContext 删除 指定Context 下的 一个属性里面包含的子属性 
         * @param context 连接后的DirContext 
         * @param cn 指定Context的名称 
         * @param attList 包含要删除的属性的名称 
         * @throws BaseException 
         * @throws NamingException 
         */ 
        public static void deleteInAttributes(DirContext ctx, String userDN, 
                                              List attList,String flag) throws NamingException { 
            if (attList == null || attList.size() == 0) { 
                return; 
            } else { 
                int size = attList.size(); 
                ModificationItem[] mods = new ModificationItem[size]; 
                for (int i = 0; i < size; i++) { 
                    Attribute att = null; 
                    mods[i] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, 
                                                   new BasicAttribute( 
                            flag, (String) attList.get(i))); 
                } 
                ctx.modifyAttributes(userDN, mods); 
            } 
     }

     /** 
      * 创建一个连接,通过捕捉Exception来确定该用户是否存在于目标ldap中 
      * @param configDto ConfigDto 
      * @param uid String 
      * @param password char[] 
      * @return boolean 
      * @throws NamingException 
      */ 
     public static boolean authenticate(ConfigDto configDto, String uid, char[] password) throws 
             NamingException { 
         Hashtable mEnvironment = new Hashtable(); 
         DirContext mContext = null; 
         //创建连接 
         mEnvironment.put(Context.INITIAL_CONTEXT_FACTORY, 
                          configDto.getEnvfactory()); 
         mEnvironment.put(Context.PROVIDER_URL, configDto.getEnvurl()); 
         mEnvironment.put(Context.SECURITY_AUTHENTICATION, "simple"); 
         mEnvironment.put(Context.SECURITY_PRINCIPAL, 
                          Constants.LDAP_PEOPLE_ATTRIBUTE_UID + "=" + uid + "," + 
                          configDto.getEnvPeopleLoc()); 
         mEnvironment.put(Context.SECURITY_CREDENTIALS, password); 
         try { 
             mContext = new InitialDirContext(mEnvironment); 
             log.debug("user:"+uid+" login!"); 
             return true; 
         } catch (AuthenticationException ex) { 
             log.error("user:"+uid+" don't login because of wrong user name or password!"); 
             return false; 
         } 
     } 
查询语法
LDAP搜索语法采用左表达式语法格式定义,其语法如下:

      ::= '(' ')'
      ::= | | |
      ::= '&'
      ::= '|'
      ::= '!'
      ::= |
      ::= | |
      ::=
      ::= | | |
      ::= '='
      ::= '~='
      ::= '>='
      ::= '<='
      ::= '=*'
      ::= '='
      ::= NULL |
      ::= '*'
      ::= NULL | '*'
      ::= NULL |

LDAP 当通过JNDI搜索时,支持LDAP搜索语法来简化搜索,LDAP搜索语法:
1 搜索语法必须以括号开始并以括号结束
2 搜索语法采用左表达式格式
3 搜索语法支持下面的逻辑运算符
运辑运算符 含义
& 与运算
| 或运算
! 非运算

4搜索语法可以包含下列等式运算符
运辑运算符 含义
= 相等运算
~= 不相等运算
> 大于运算
< 小于运算
>= 大于等于运算
<= 小于等于运算

5 搜索语法还可以支持通配符(*)和空(NULL)判断运算
运辑运算符 含义
* 0或多个字符
NULL 空
例如:
1 &(cn=lwf)(ou=mah)
表示查询属性cn=lwf 且 ou=mah 的节点
2 (ou=mah*)
表示查询属性ou以mah开头的节点

要获取更多关于 LDAP 搜索过滤器语法的信息,请参阅 RFC 1960
第三节 访问LDAP数据的API—JLDAP
JLDAP是由novel开发的,原是针对Novel的NDS目录设计的JAVA访问工具。NOVEL的NDS和网景(NETSCAPE)的目录是工具界最早的目录产品。JLDAP并非JNDI的服务供应者,而是同一抽象层次下的访问工具集。与JNDI-LDAP相比,JLDAP更接近于类关系数据库的访问方式。
NDS是遵守LDAP协议的并进行了扩展的类MAD产品。而NOVEL也已把JLDAP捐献给了OPENLDAP开源项目,可以世界范围内自由使用。与JNDI相比,JLDAP无须继承DirContext才能实现添加,也无需预先生成添加的类,可以象普通数据访问那样,生成连接,然后使用::add方法添加。这样,添加的灵活性要强于JNDI。
但由于JLDAP目前是访问NDS,因此,它不具备JNDI完全面向对象存储的能力,对于高级的LDAP应用,JLDAP不是合适的选择。
例:
import com.novell.ldap.*;
public class AddEntry
{
     public static void main( String[] args )
     {
         if (args.length != 4) {
             System.err.println("Usage:   java AddEntry "
                                                 + " ");
             System.err.println("Example: java AddEntry Acme.com"
                         + " /"cn=admin,o=Acme/" secret /"ou=Sales,o=Acme/"");
             System.exit(1);
         }
         int ldapPort = LDAPConnection.DEFAULT_PORT;
         int ldapVersion  = LDAPConnection.LDAP_V3;
         String ldapHost       = args[0];
         String loginDN        = args[1];
         String password       = args[2];
         String containerName  = args[3];
         LDAPConnection lc = new LDAPConnection();
         LDAPAttribute  attribute = null;
         LDAPAttributeSet attributeSet = new LDAPAttributeSet();
         /* To Add an entry to the directory,
          *   -- Create the attributes of the entry and add them to an attribute set
          *   -- Specify the DN of the entry to be created
          *   -- Create an LDAPEntry object with the DN and the attribute set
          *   -- Call the LDAPConnection add method to add it to the directory
          */          
         String objectclass_values[] = { "inetOrgPerson" };
         attribute = new LDAPAttribute( "objectclass", objectclass_values );
         attributeSet.add( attribute );     
         String cn_values[] = { "James Smith", "Jim Smith", "Jimmy Smith" };
         attribute = new LDAPAttribute( "cn", cn_values );
         attributeSet.add( attribute );
         String givenname_values[] = { "James", "Jim", "Jimmy" };
         attribute = new LDAPAttribute( "givenname", givenname_values );
         attributeSet.add( attribute );
         attributeSet.add( new LDAPAttribute( "sn", "Smith" ) );
         attributeSet.add( new LDAPAttribute( "telephonenumber",
                                                      "1 801 555 1212" ) );
         attributeSet.add( new LDAPAttribute( "mail", "JSmith@Acme.com" ) );
         String  dn  = "cn=JSmith," + containerName;     
         LDAPEntry newEntry = new LDAPEntry( dn, attributeSet );
         try {
             // connect to the server
             lc.connect( ldapHost, ldapPort );
             // authenticate to the server
             lc.bind( ldapVersion, loginDN, password );
             lc.add( newEntry );
             System.out.println( "/nAdded object: " + dn + " successfully." );
             // disconnect with the server
             lc.disconnect();
         }
         catch( LDAPException e ) {
             System.out.println( "Error:  " + e.toString());
         }                                  
         System.exit(0);
     }
}
第四节 访问LDAP数据的API-JDBCLDAP
JDBCLDAP是OcterString提供的,能过类SQL实现LDAP访问的工具。
1 JDBCLDAP的出现解决了许多人操作LDAP的方便性,但是我们必须知道他的优势和劣势:
i JDBCLDAP 使熟悉SQL的程序员方便操作LDAP目录数据,但是其SQL与数据库的SQL还存在一个的差异。
ii JDBCLDAP 只是完成了简单的查询,插入,更新,删除等操作,对于联合查询,事务性无法实现(LDAP不支持事务)。
iii Jdbc-LDAP不可以完成串行化binding。
因此,本人的观点是JDBCLDAP只适宜对已有的LDAP系统进行临时访问(如程序员不熟悉,或保持旧程序,仅修改必要的连接项),不宜把整个项目建筑在JDBC-LDAP上。实际上,LDAP本身就是一种访问的前端协议,硬要把SQL再作为前端使用,是完全没有必要的。但是分析JDBCLDAP如何实现JDBC的interface是很有用处的。
2 JDBCLDAP包分析
包 com.octetstring.jdbcLdap.browser
提供一个使用JDBCLDAP操作目录的界面
包 com.octetstring.jdbcLdap.junit
一些JDBCLDAP的junit测试程序
包 com.octetstring.jdbcLdap.jndi
包 com.octetstring.jdbcLdap.sql
包 com.octetstring.jdbcLdap.util
3 JDBCLDAP 操作目录数据库
JDBCLDAP 实现 java.sql.Driver 的驱动程序实现类为 com.octetstring.jdbcLdap.sql.JdbcLdapDriver
JDBCLDAP有一套标准的连接URL格式,如下
jdbc:ldap|jdbc:dsmljdbc:spml://[Host或IP]:[端口号]/[节点DN]?[参数名]:=[参数值]
说明:
jdbc:ldap|jdbc:dsml|jdbc:spml : JDBCLDAP 连接URL前缀,可写成jdbc:ldap,jdbc:dsml,jdbc:spml 其中的任何一个.
Host或IP 为运行 目录服务的机器的名字或者IP地址
端口号 为运行 目录服务的机器的端口号
节点DN 为 登录后的基DN
参数名,参数值 为JDBCLDAP URL 定义的一些参数,支持的参数如下表
JDBCLDAP URL 连接参数
参数名 参数值 含义
user 用户名 登录目录服务用户名
password 密码 登录目录服务用户密码
SIZE_LIMIT  
TIME_LIMIT  
java.naming.authentication simple 简单认证方式
none 无认证方式
SASL SASL认证方式 认证方式
SEARCH_SCOPE objectScope 只查看object属性
oneLevelScope 只查看一层
subTreeScope 查看子树 查询范围
CONCAT_ATTS  
CACHE_STATEMENTS true statement 使用缓存
false statement 不使用缓存  是否缓存statement
JDBCLDAP 的一些操作,JDBCLDAP 将LDAP目录树上的节点看作一个表,节点的属性看作表中的字段
i simple认证方式连接目录数据库
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
  String ldapConnectString =
            "jdbc:ldap://yzxp:13221/dc=4adomain,dc=com?SEARCH_SCOPE:=subTreeScope";
     java.sql.Connection con;
Connection    con = DriverManager.getConnection(ldapConnectString, "cn=Directory Manager", "12345678");
ii 增加一个节点
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
String ldapConnectString =
             "jdbc:ldap://yzxp:13221/dc=4adomain,dc=com?SEARCH_SCOPE:=subTreeScope";
     java.sql.Connection con;
  Connection   con = DriverManager.getConnection(ldapConnectString, "cn=Directory Manager", "12345678");

     String sql = "INSERT INTO uid=liaowf,ou=People,dc=test4a,dc=com "+
                  "(objectClass,objectClass,objectClass,objectClass,cn,givenName,sn,uid,userPassword) " +
                  "VALUES (top,person,organizationalPerson,inetorgperson,liao wufeng,wufeng,liao,liaowf,liaowufeng)";

     Statement insert = con.createStatement();
    int count = insert.executeUpdate(sql);

     if (count < 1) {
         System.out.println("Insert Failed");
     } else {
         System.out.println("Insert Succeeded");
     }

    if(con!=null)
    con.close();
iii 查询一个节点
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
String ldapConnectString =
                 "jdbc:ldap://yzxp:13221/dc=test4a,dc=com?SEARCH_SCOPE:=subTreeScope";
         java.sql.Connection con;
   Connection      con = DriverManager.getConnection(ldapConnectString,
                                           "cn=Directory Manager", "12345678");

         String sql = "SELECT cn,sn,uid FROM ou=People,dc=test4a,dc=com WHERE uid=zyu OR uid=liaowf";

         Statement sta = con.createStatement();
         ResultSet rs = sta.executeQuery(sql);
         String cn = null;
         String sn = null;
         String ou = null;
         while(rs.next()){
             cn = rs.getString(1);
             sn = rs.getString(2);
             ou = rs.getString(3);
             System.out.println("cn="+cn+" sn="+sn+" ou="+ou);
         }
if(rs!=null)
             rs.close();
if(con!=null)
             con.close();
iv 修改一个节点
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
String ldapConnectString =
            "jdbc:ldap://yzxp:13221/dc=4adomain,dc=com?SEARCH_SCOPE:=subTreeScope";
    java.sql.Connection con;
Connection   con = DriverManager.getConnection(ldapConnectString, "cn=Directory Manager", "12345678");
String sql = "UPDATE uid=liaowf,ou=People,dc=test4a,dc=com SET "+
                 "userPassword= " +
                 "liaowufeng";
Statement insert = con.createStatement();
   int count = insert.executeUpdate(sql);
if (count < 1) {
        System.out.println("Insert Failed");
    } else {
        System.out.println("Insert Succeeded");
    }
if(con!=null)
   con.close();
v 删除一个节点
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
     String ldapConnectString =
             "jdbc:ldap://yzxp:13221/dc=4adomain,dc=com?SEARCH_SCOPE:=subTreeScope";
     java.sql.Connection con;
Connection    con = DriverManager.getConnection(ldapConnectString, "cn=Directory Manager", "12345678");
     String sql = "DELETE uid=liaowf,ou=People,dc=test4a,dc=com";
     Statement insert = con.createStatement();
    int count = insert.executeUpdate(sql);
     if (count < 1) {
         System.out.println("Insert Failed");
     } else {
         System.out.println("Insert Succeeded");
     }
    if(con!=null)
    con.close();

vi JDBC查询语法与LDAP查询语法转法工具类
   String expr = "F1=1 AND F2=2 OR (f3=3 OR f4=4) AND NOT f5=5";
              SqlToLdap trans = new SqlToLdap();
              trans.convertToLdap(expr,null);

第六章 Sun One Dirctory Server 专题
(以Sun One Directory Server 5.2版本)
第一节 安装
1 安装前准备
在安装Sun One Directory Server 之前,我们必须给我们将运行的网络拓朴作一个规划,如哪台服务器安装Web服务,哪台服务器安装数据库服务,及目录服务安装在什么域和什么IP上等等。
下面我们将要安装的环境的准备信息,如下表

安装信息
目录管理域名 test.com
Administration Server 端口号 5201
目录管理员ID admin
目录管理员密码 123456789
目录管理员DN cn=Directory Manager
目录管理员DN密码 12345678
Directory Server 端口号 389
完全限定的主机标识名称 liaowf.test.com
服务器后缀 dc=test,dc=com
完整的计算机名称 liaowf.test.com

当准备好网络拓朴信息好,下面,我们将开始安装Sun One Directory Server服务器
2 更改服务器的主DNS后缀
从我的电脑的属性进入修改服务器的主DNS后缀为test.com,修改后,服务器的完整的计算机名称为liaowf.test.com,修改后需要重启服务器.如下图
3 运行安装文件中的setup.exe 文件
开始安装(安装文件不要放在含有中文的文件路径中,否则,安装会不成功;安装未完成时,请不要关掉后的安装黑窗口)
运行setup.exe 安装文件

出来欢迎安装界面

许可界面

录入完整的计算机名

安装目录服务器和客户端,所以应选择 Sun ONE Serves ,如果只是安装客户端,就选择 Sun ONE Server Console ,这里我们选择 Sun ONE Servers ,因为服务器端和客户端都需要安装.
安装类型,我们选择Typical 典型

选择安装目录

选择要安装的组件

选择是创建一个新的目录服务器配置,还是使用已存在的目录服务器配置,这是我们选择创建一个新的目录服务器
选择目录数据存储在新的数据库,还是选择一个已存在的目录服务库存储,这时选择存储在新的数据库

录入 目录服务器server identifier,server port,suffic.
录入目录服务管理员ID,密码

录入 目录服务管理的域

录入目录服务管理员DN,密码

录入管理端口

确认安装信息

开始安装

安装时信息回显示

安装完成
第二节 卸载
找到安装Sun One Directory Server 目录,运行 uninstall_dirserver.exe文件

第三节 一些安装常见安装的问题及解决
1 安装时,要求录入完整的计算机名,我录入后,总是提示找不到请确认安装时,已更改服务器的DNS后缀
2 安装过程中,提示安装不成功,后面的黑窗口提示 StringIndexOutOfBoundsException 异常。请确认你的目当服务器安装文件,没有放在含有中文的文件路径中 
3 目录服务器安装后,我忘记了目录管理员DN和口令。
按如下步骤,可以解决
目录管理员的DN 存储在
ServerRoot/slapd-serverID/config/dse.ldif 中,目录管理员 DN 被记
录为 nsslapd-rootdn 值。
目录管理员口令存储在
ServerRoot/slapd-serverID/config/dse.ldif 中,目录管理员口令被记录为 nsslapd-rootpw 值。如果口令没有加密,则它会以明文的形式.
如果口令已加密,则会带有用加密方案标识符表示的前缀(如 {SSHA})。
我们必须按下面步骤手动修复加密码口令:
1. 停止 Directory Server。
2. 更改 dse.ldif 中 nsslapd-rootpw 的值,注意不要添加尾空格。
3. 保存并关闭 dse.ldif。
4. 重新启动服务器。
5. 使用分配给 nsslapd-rootpw 的值,以目录管理员的身份登录。
6. 登录成功后,在管理界面中设置目录管理员口令的加密方案,更改口令。
第四节 管理经验
Sun One Directory Server 5.2 提供了一个很好的管理文档
<>, 下面是这篇文档的大纲内容
一 启动和停止Sun ONE  Directory Serve
二 创建目录树
三 填充目录内容
四 高级条目管理
五 管理访问控制
六 用户帐户管理
七 扩展目录模式
八 管理索引
九 管理日志文件
第五节 Sun ONE 安装文件分析

第六节 使用目录服务经验
   1 新建域目录
   2 导入/导出域目录数据
   3 快捷备份
3 新建schema,自定义attribute
5 如何使用Sun One 目录服务器建立一个简单用户管理系统
   1 设计用户schema 系统
2 在Sun One 目录服务器上,建schema,attribute
3 选择目录服务访问技术
4 编写实现

第七章 微软 Active Directory Server 专题
第一节 JAVA通过LDAP修改windows Active Directory 域用户密码
1 注意:
1 .LDAP 无法获取windows Active Directory 用户密码
2. 系统管理员可以修改其他用户的密码(不需要知道原来的旧密码),或者用户可以修改自己的密码(用户必须知道自己的密码)。
这些密码修改操作必须通过一个安全通道来执行,如SSL、TLS、Kerberos。
3. Windows 2000 域控制器不支持TLS协议。但是Windows 2000 和 Windows Server 2003 域控制器都支持SSL。
4.对基于SSL或TLS的会话,你的工作站(或指定的JRE)必须信任域控制器认证中心发布的CA证书。
5.相关资料可到www.ldapchina.com网站在看

2 具体步骤:
i 环境要求:
一台安装Active Directory 的服务器,域名为security.boco
一台安装证书服务(需安装企业根证书)的服务器,此服务器加入security域中
一台安装JAVA应用的服务器,此服务器不需要加入security域中
ii 安装步骤:
iii 安装Active Directory 域控制器
iv 安装证书服务
v 以域用户登录到安装了证书服务的服务器中,导出域根证书和计算机证书
第一步:进入MMC控制台,添加证书,选择本地计算机
第二步展开刚增加的证书节点,选择证书个人->证书,选择CA证书,导出
第三步展开证书节点,选择证书个人证书 ,右击所有任务,申请新证书,证书类型选择计算机类型
第四步 将从证书中导出的两个证书文件,*.cer 使用java的keytool工具创建或导入证书库文件中
vi 导入CA证书
D:/Borland/jdk142_05/bin>keytool -import -keystore security51.keystore -file 51A
Droot.cer
输入keystore密码:  lwfmah
Owner: CN=securityCA, DC=security, DC=boco
发照者: CN=securityCA, DC=security, DC=boco
序号: 72880fb3005cd7a54efa9c224241008b
有效期间: Thu Nov 10 20:48:49 CST 2005 至: Tue Nov 10 20:55:33 CST 2015
认证指纹:
          MD5:  51:3F:C3:B1:C3:A6:EF:24:55:70:2A:25:0D:EB:57:59
          SHA1: B3:EE:CC:92:E3:D4:87:48:D4:1D:F3:53:5B:0E:99:E1:B7:0F:27:20
信任这个认证? [否]:  y
认证已添加至keystore中
导入申请的计算机证书
D:/Borland/jdk142_05/bin>keytool -import -keystore security51.keystore -alias co
mkey -file 51AD.cer
输入keystore密码:  lwfmah
认证已添加至keystore中

vii 编写如下代码修改Active Direcotry 域用户密码
   public static void main(String[] args) throws UnknownHostException,
             IOException {
// java.net.Socket sock = new java.net.Socket("10.110.180.50",636);
// boolean b = sock.isConnected();
   Hashtable env = new Hashtable();
   String adminName = "cn=administrator,cn=users,DC=security,DC=boco";
   String adminpassword = "123456789";
   String userName = "CN=iam_lwf_count,OU=网管中心,DC=security,DC=boco";
  // old password Ab123456
   String newPassword = "liaowufeng";
   String keystore = "D:/Borland/jdk142_05/bin/testAD51.keystore";
  //  String keystore = "E:/project/iam/testADlhj.keystore";
System.setProperty("javax.net.ssl.trustStore", keystore);
   System.setProperty("javax.net.ssl.trustStorePassword", "boco123");
         env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
         env.put(Context.SECURITY_AUTHENTICATION, "simple");
         env.put(Context.SECURITY_PRINCIPAL, adminName);
         env.put(Context.SECURITY_CREDENTIALS, adminpassword);
         env.put(Context.SECURITY_PROTOCOL, "ssl");
         String ldapURL = "ldaps://10.110.180.50:636";
         env.put(Context.PROVIDER_URL, ldapURL);
         try {
             LdapContext ctx = new InitialLdapContext(env, null);
             ModificationItem[] mods = new ModificationItem[1];
             String newQuotedPassword = "/"" + newPassword + "/"";
             byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
             mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("unicodePwd",newUnicodePassword));
             ctx.modifyAttributes(userName, mods);
             System.out.println("Reset Password for: " + userName);
             ctx.close();
              System.out.println("Problem encoding password222: ");
         } catch (Exception e) {
             e.printStackTrace();
             System.out.println("Problem encoding password222: " + e);
         }
}
第八章 LDAP技术资源
第一节 介绍LDAP的Roadmap & FAQ,
http://www.kingsmountain.com/ldapRoadmap.shtml

第二节 免费ldap server,
http://www.openldap.org

第三节 开源的ldap server
http://javaldap.sourceforge.net/

第四节 Java 开发 jndi
http://www.cris.com/~adhawan/tutorial/ 
http://java.sun.com/products/jndi/tutorial/

第五节 Java 开发 jldap来 开发
http://www.openldap.org/jldap/

第六节 java 开发JDBC-LDAP来开发,
http://www.openldap.org/jdbcldap/
http://www.octetstring.com/products/BridgeDriver.php

第七节 ldapguru 
http://www.ldapguru.com/

第八节 LDAPChina.com 论坛
http://www.ldapchina.com/
http://phorum.study-area.org/viewforum.php?f=24

第九节 LDAP Browser/Editor 客户端工具
http://www-unix.mcs.anl.gov/~gawor/ldap/
http://www-unix.mcs.anl.gov/~gawor/ldap/

第十节 phpGeneral php实现的客户端工具
http://www.linuxnetworks.de/general/index.html

第十一节 LDAP Administrator 可以修改schema
http://www.ldapbrowser.com/

第十二节 LDAPNavigator 基于php
http://sourceforge.net/projects/ldapnavigator/

第十三节 Web2ldap
http://www.web2ldap.de/

第十四节 ldap 做的user manage CA 赞助的
http://ldapusrmgr.sourceforge.net/

第十五节 Softerra LDAP Administrator
http://download.softerra.com/ldap/index.html