Spring LDAP

   由于公司最近要启用一个 LDAP项目,并要求去学习,(好复杂的一个目录服务器。。),还好代码不难,下面就解读下帮助中案例。纯属于个人理解而总结出来的,并非官方结论,仅供参考!!

   首先先说配置文件,一个简单的spring配置文件 注入到dao中

<beans>
   <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
      <property name="url" value="ldap://localhost:389" />
      <property name="base" value="dc=example,dc=com" /><!-- 这里是服务的根地址, 可以在这里配置,也可在项目里配置,主要看项目要求-->
      <property name="userDn" value="cn=Manager" />
      <property name="password" value="secret" />
   </bean>
   <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
      <constructor-arg ref="contextSource" />
   </bean>
   <bean id="personDao" class="com.example.dao.PersonDaoImpl">
      <property name="ldapTemplate" ref="ldapTemplate" />
   </bean>
</beans>


帮助文档中开篇的那一大串的代码就不详细说,碰到再解释,下面为简化后的那段代码。

package com.example.dao;
public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
    /*注入*/
   public void setLdapTemplate(LdapTemplate ldapTemplate) {
      this.ldapTemplate = ldapTemplate;
   }
   /** 可返回所有根地址,对象类型为persopn的信息的cn属性值*/
   public List getAllPersonNames() {
    /*search方法中设计到三个参数,第一个是dn(类似于路径url),
     * 这里为空是因为 我们之前XML中已经配置了根地址,
     * 第二个就是对象类型,第三个是要查询之后返回的信息*/
      return ldapTemplate.search( "", "(objectclass=person)",
         new AttributesMapper() {
            public Object mapFromAttributes(Attributes attrs)
               throws NamingException {
               return attrs.get("cn").get();
            }
         });
   }
}

最后会得到一个List集合 里面存有"CN"对应的值。


 接下来就是第二章的简单增删改查功能,首先根据文档继续说下查找的功能,文档描述的 我看了N遍才弄懂(好笨。。)。

 上面说到search的方法需要三个参数,接下来就对它的这三个参数进行一个简单的封装。

首先是封装dn,我理解为url地址。文档中是这样封装放在想在

package com.example.dao;
import org.springframework.ldap.core.support.DistinguishedName;
import javax.naming.Name;
public class PersonDaoImpl implements PersonDao {
  /*这里就是我之前在XML中提到的可以把根目录放在项目中使用*/
   public static final String BASE_DN = "dc=example,dc=com";
   /*定义个dn的方法,返回类型是Name形式*/
   protected Name buildDn(Person p) {
      DistinguishedName dn = new DistinguishedName(BASE_DN);//初始化时可以把根写上,也可写放在下面。
      dn.add("c", p.getCountry());   //跟前面的路径(国家)
      dn.add("ou", p.getCompany());  //c前面的路径(公司)
      dn.add("cn", p.getFullname()); //在ou前面的路径(你创建的该信息的路径)
      return dn; //创建好之后就会返回一个dn可以直接放在search中
   }
}
dn的样式大致是这样的 :
cn=Some Person, ou=Some Company, c=Sweden, dc=example, dc=com

这个路径就是你将来也许会用很多次的路径 。。。

不过这里有跟致命的问题 当先去查找P的属性 再去更新,新增的时ou和c的属性是查不到的,这里坑了我半天,我后来也没有去测试如何应该把ou和c的属性放在p的属性了,因为我一直没找到对应的属性名字,所以我后来直接把路径写成常量。。。(有知道请回复,3q)

接下来说第二个参数的封装

AndFilter filter = new AndFilter();
/*可以理解为查询条件的过滤器,比如sql中的where子句,但下面的关于objectclass的属性是必须写的,理解为1=1吧*/
filter.and(new EqualsFilter("objectclass", "person"));
/*这里创建里一个模糊查询条件方法并输入查询条件,该方法同等于
new LikeFilter("cn","*cn*")方法,可以理解为sql中的like子句*类似% */
filter.and(new WhitespaceWildcardsFilter("cn", "cn"));
上吧我把第二个cn处多家了二个" 是为了让大家知道这里是字符串格式

下面我说就说下第三个参数的封装吧

package com.example.dao;
public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
    /*创建一个结果类,继承AttributesMapper接口,重写他的方法*/
   private class PersonAttributesMapper implements AttributesMapper {
      public Object mapFromAttributes(Attributes attrs) throws NamingException {
     /*这里创建一个person对象是为了返回时可以直接返回实体,如果多个实体,他会成集合形式*/
         Person person = new Person();
/*这里是将你查找到的信息中属性名字对应的值放在你的实体对象中,并一同返回。*/
         person.setFullName((String)attrs.get("cn").get());
         person.setLastName((String)attrs.get("sn").get());
        person.setDescription((String)attrs.get("description").get());
         return person;//返回你的实体
      }
   }
    /*文档中的案例,使用结果类的方法*/
   public List getAllPersons() {
      return ldapTemplate.search("", "(objectclass=person)", new PersonAttributesMapper());
   }
}

以上就是search方法中的三个参数封装,学的时候真蒙啊,现在看看来不过如此。


接下来讲下新增 bind方法

在刚才我们封装查找的三个参数,下面新也同样有三个方法:

1.dn(老相识了),2.一个object类型的参数 文档给的是null,真心不知道是什么玩应(不理会)

3.一个属性的类,要创建的属性都在这里。

package com.example.dao;
public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public void create(Person p) {//创建方法
      Name dn = buildDn(p);    //调用之前封装dn的方法获得dn
      ldapTemplate.bind(dn, null, buildAttributes(p));
   }
  /*设置需要创建的属性,objectclass的对象类属性应该是必有的*/
   private Attributes buildAttributes(Person p) {
      Attributes attrs = new BasicAttributes(); //创建一个属性集
      BasicAttribute ocattr = new BasicAttribute("objectclass");
      ocattr.add("top");   //这个属性数必须有,就算在服务器手写创建代码时也需要添加它
      ocattr.add("person"); //创建的对象类
      attrs.put(ocattr);    //将属性集添加到属性中
      attrs.put("cn", "Some Person");  //输入 cn属性  对应后台的姓的值
      attrs.put("sn", "Person"); // 对应后台的常用名
      return attrs;  //返回你获得的属性
   }
}

虽然创建的方法bind中需要是三个参数,但是实际使用的两个参数,第一个为地址,第二个null,第三个是要插入的属性


接下来我们看下 删除,这个是简单的方法,可以理解为DAO中根据id进行删除一个实体就OK了

package com.example.dao;
public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public void delete(Person p) {
      Name dn = buildDn(p); //根据你的用户信息获得你的dn
      ldapTemplate.unbind(dn); //根据路径删除这条信息
   }
}

接下来就更新了,有两成方式,一种是暴力型,一种是简单更换型。

下面的是暴力型

package com.example.dao;
public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public void update(Person p) {
      Name dn = buildDn(p);
      ldapTemplate.rebind(dn, null, buildAttributes(p));
   }//只有rebind方法名不同,看名字就知道是重新绑定用户信息,意思是先kill掉用户然后重新添加一个,(太掺不忍睹了,没考虑过性能,如果方便的话貌似也可以。。。。。。。)
}

接下来就是真正的更新。

package com.example.dao;
public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;
   ...
   public void updateDescription(Person p) {
      Name dn = buildDn(p);
/*同样也封装了个修改的方法,但是他把这个方法放在了里面*/
/*设置修改的属性,但这里只能修改一个属性就新建一个不能用Attributes(至少我没用过,因为后面有更好的方法)*/
      Attribute attr = new BasicAttribute("description", p.getDescription())
/*修改的类 将需要修改的属性放在里面*/
      ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
/*进行修改 创建一个修改条件,他是ModificationItem[]数组格式 所以要同时更新两个属性 就要 创建两个ModificationItem 然后放进数组 */
      ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});
   }
}


OK这就是简单增删改查。

(未完待续......下篇预告[再次简化LDAP操作帮助第三章])