Spring Ldap
- Spring Ldap是一个用来简化LDAP操作的开源框架
- Spring Ldap中的ODM技术可以像Hibernate的ORM一样将对象映射到AD域上
- 该框架通过提供和ORM中相似的机制对LDAP相关操作进行封装,主要包括:
- 类比SessionFactory的LdapContextSource
- 类比HibernateTemplate等的LdapTemplate
- 伪事务支持,能否与tx框架的TransactionManager混用未知
- 类比JPA的使用@Entry、@Attribute、@Id标注的ODM——Object Directory Mapping
- 本文的目的是通过Ldap操作获取AD域的组和用户
- 文档:http://docs.spring.io/spring-ldap/docs/2.0.4.RELEASE/reference/
本文主要使用ODM技术映射对象,简化ObjectMapping操作,当然也可以不定义对象自己定义AttributesMapper,相关内容可以查看官方文档。
ORM映射
1.映射组织单位organizationalUnit
@Entry(objectClasses = { "organizationalUnit", "top" })
public final class Group {
@Id
@JsonIgnore // Name无法完全被转换成json字符串,所以不要转换
private Name dn;
@Attribute(name = "ou")
private String groupName;
@Attribute(name = "description")
private String description;
// getter setter tostring...
}
2.映射用户
@Entry(objectClasses = { "organizationalPerson", "person", "top", "user" })
public class Person {
@Id
@JsonIgnore
private Name dn;
@Attribute(name = "cn")
private String fullName;
@Attribute(name = "sn")
private String lastName;
@Attribute(name = "mail")
private String mail;
@Attribute(name = "managedObjects")
private String managerObject;
@Transient
private String group;
@Attribute(name = "description")
private String description;
// getter setter tostring...
}
关于ODM的解释:
- @Entry定义一个Entry对象,映射AD域的对象信息, objectClasses 指定对象类属性,是一个数组
- @Id 类似于ORM中的@Id,但是在这里是指DN值(在AD域中DN值是唯一的)
- @Attribute指定Entry中的某一个属性值,没有指定@Attribute的属性为改属性名
- @Transient表示该属性不映射AD域中的某个属性
- @DnAttribute(value=”ou”, index = 1) 表示将DN值中的某一个值取出作为该变量值
下面定义一个Repository访问AD域信息:
@Component
public class CustomLdapRepositoryImpl implements CustomLdapRepository {
private LdapTemplate ldapTemplate;
// open a ldap connection
private void openLdapConnection(LdapGroupServerInfo serverInfo) {
String url = "LDAP://" + serverInfo.getIp() + ":"
+ serverInfo.getPort();
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(url);
contextSource.setUserDn(serverInfo.getDnName());
contextSource.setPassword(new String(Base64.decode(serverInfo
.getPassword().getBytes())));
contextSource.setReferral("follow");
contextSource.afterPropertiesSet();
ldapTemplate = new LdapTemplate(contextSource);
ldapTemplate.setIgnorePartialResultException(true);
}
@Override
public List<Group> findGroupsByBase(LdapGroupServerInfo serverInfo,
String base, int searchScope) {
openLdapConnection(serverInfo);
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(searchScope);
LdapName ldapName = null;
try {
ldapName = new LdapName(base);
} catch (InvalidNameException e) {
e.printStackTrace();
}
return ldapTemplate.findAll(ldapName, searchControls, Group.class);
}
@Override
public List<Person> findPersonsByBase(LdapGroupServerInfo serverInfo,
String base, int searchScope) {
openLdapConnection(serverInfo);
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(searchScope);
LdapName name = null;
try {
name = new LdapName(base);
} catch (InvalidNameException e) {
e.printStackTrace();
}
return ldapTemplate.findAll(name, searchControls, Person.class);
}
@Override
public List<Person> findPersonsByEmail(LdapGroupServerInfo serverInfo,
String base, String email) {
openLdapConnection(serverInfo);
// if no detail base attr, use server base to find
if (base == null) {
base = serverInfo.getBase();
}
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
LdapName name = null;
try {
name = new LdapName(base);
} catch (InvalidNameException e) {
e.printStackTrace();
}
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("mail", email));
return ldapTemplate.find(name, filter, searchControls, Person.class);
}
@Override
public List<Person> findManagersByPersonDN(LdapGroupServerInfo serverInfo,
String personDN) {
openLdapConnection(serverInfo);
personDN = personDN.substring(personDN.indexOf(",") + 1);
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
LdapName name = null;
try {
name = new LdapName(personDN);
} catch (InvalidNameException e) {
e.printStackTrace();
}
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("managedObjects", personDN));
return ldapTemplate.find(name, filter, searchControls, Person.class);
}
@Override
public boolean authenticateUser(LdapGroupServerInfo serverInfo,
String userDN, String credentials) {
openLdapConnection(serverInfo);
DirContext ctx = null;
try {
ctx = ldapTemplate.getContextSource().getContext(userDN,
credentials);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
LdapUtils.closeContext(ctx);
}
}
...
}
上文主要是通过ldapTemplate来进行数据请求和处理的,需要先定义一个方法来开启ldap连接,通过contextSource来建立连接:
contextSource.setReferral(“follow”) 我们只需要部分数据,这个设置可以防止其报javax.naming.PartialResultException异常。
follow模式在多层级下会影响读取速度,可以设置为ignore。
contextSource.afterPropertiesSet() 表示不是基于配置模式来配置contextSource的。
至此,我们就可以通过这个Repository来进行数据交互了,当然还可以继续扩展改类来实现更多的功能。