详细概念参考:详细介绍
DAP的英文全称是Lightweight Directory Access Protocol,简称为LDAP。LDAP是轻量目录访问协议。目录服务是一个特殊的数据库,用来保存描述性的、基于属性的详细信息,支持过滤功能。它成树状结构组织数据,类似文件目录一样。目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。
LDAP是开放的Internet标准,支持跨平台的Internet协议,在业界中得到广泛认可的,并且市场上或者开源社区上的大多产品都加入了对LDAP的支持,因此对于这类系统,不需单独定制,只需要通过LDAP做简单的配置就可以与服务器做认证交互。“简单粗暴”,可以大大降低重复开发和对接的成本。
每一个系统、协议都会有属于自己的模型,LDAP也不例外,在了解LDAP的基本模型之前我们需要先了解几个LDAP的目录树概念:
(一)目录树概念
目录树:在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目。
条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)。
对象类:与某个实体类型对应的一组属性,对象类是可以继承的,这样父类的必须属性也会被继承下来。
属性:描述条目的某个方面的信息,一个属性由一个属性类型和一个或多个属性值组成,属性有必须属性和非必须属性。
(二)DC、UID、OU、CN、SN、DN、RDN
关键字 | 英文全称 | 含义 |
---|---|---|
dc | Domain Component | 域名的部分,其格式是将完整的域名分成几部分,如域名为example.com变成dc=example,dc=com(一条记录的所属位置) |
uid | User Id | 用户ID songtao.xu(一条记录的ID) |
ou | Organization Unit | 组织单位,组织单位可以包含其他各种对象(包括其他组织单元),如“oa组”(一条记录的所属组织) |
cn | Common Name | 公共名称,如“Thomas Johansson”(一条记录的名称) |
sn | Surname | 姓,如“许” |
dn | Distinguished Name | “uid=songtao.xu,ou=oa组,dc=example,dc=com”,一条记录的位置(唯一) |
rdn | Relative dn | 相对辨别名,类似于文件系统中的相对路径,它是与目录树结构无关的部分,如“uid=tom”或“cn= Thomas Johansson” |
// 只要不抛出异常就是验证通过
public LdapContext adLogin(String username,String password) {
String server = ldapConfig.getUrl();
try {
Hashtable<String, String> env = new Hashtable<String, String>();
//用户名称,cn,ou,dc 分别:用户,组,域
env.put(Context.SECURITY_PRINCIPAL, username);
//用户密码 cn 的密码
env.put(Context.SECURITY_CREDENTIALS, password);
//url 格式:协议://ip:端口/组,域 ,直接连接到域或者组上面
env.put(Context.PROVIDER_URL, server);
//LDAP 工厂
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
//验证的类型 "none", "simple", "strong"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
LdapContext ldapContext = new InitialLdapContext(env, null);
logger.info("ldapContext:" + ldapContext);
logger.info("用户" + username + "登录验证成功");
return ldapContext;
} catch (NamingException e) {
logger.info("用户" + username + "登录验证失败");
logger.info("错误信息:"+e.getExplanation());
return null;
}
}
public List<UserLdap> getUserKey(String name){
//通过配置获取ldap获取账户名 也可以直接写自己的密码 我写在了yml配置
String userName = ldapConfig.getUserName();
//获取密码
String password = ldapConfig.getPassword();
logger.info("需要查询的ad信息:{}",name);
List<UserLdap> resultList = new ArrayList<>();
//连接到域控
LdapContext ldapContext = adLogin(userName,password);
if (ldapContext!=null){
try {
// 域节点
String searchBase = rootDnFromDomain(ldapConfig.getDomain());
// LDAP搜索过滤器类
//cn=*name*模糊查询
//cn=name 精确查询
// String searchFilter = "(objectClass="+type+")";
//查询域帐号
String searchFilter = "(sAMAccountName=*"+name+"*)";
// 创建搜索控制器
SearchControls searchCtls = new SearchControls();
String returnedAtts[]={"mobile","telephoneNumber","displayname","sAMAccountName"};
//设置指定返回的字段,不设置则返回全部
searchCtls.setReturningAttributes(returnedAtts);
// 设置搜索范围 深度
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// 根据设置的域节点、过滤器类和搜索控制器搜索LDAP得到结果
NamingEnumeration answer = ldapContext.search(searchBase, searchFilter,searchCtls);
// 初始化搜索结果数为0
int totalResults = 0;
int rows = 0;
// 遍历结果集
while (answer.hasMoreElements()) {
// 得到符合搜索条件的DN
SearchResult sr = (SearchResult) answer.next();
String dn = sr.getName();
// 得到符合条件的属性集
Attributes Attrs = sr.getAttributes();
if (Attrs != null) {
try {
UserLdap lu = new UserLdap();
++rows;
for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore();) {
// 得到下一个属性
Attribute Attr = (Attribute) ne.next();
String id = Attr.getID();
String value = Attr.get().toString();
if ("displayName".equals(id)){
lu.setUsername(value);
}else if ("sAMAccountName".equals(id)){
lu.setsAMAccountName(value);
}else if ("mobile".equals(id)){
lu.setMobile(value);
}else if ("telephoneNumber".equals(id)){
lu.setTelephoneNumber(value);
}
}
if (lu.getMobile()!=null &&lu.getsAMAccountName()!=null &&lu.getUsername()!=null &&lu.getTelephoneNumber()!=null ){
resultList.add(lu);
}
} catch (NamingException e) {
logger.info("Throw Exception : " + e.getMessage());
}
}
}
logger.info("总共用户数:" + rows);
} catch (NamingException e) {
logger.info("Throw Exception : " + e.getMessage());
}finally {
try{
ldapContext.close();
}catch (Exception e){
logger.error("ldapContext关闭错误" + e);
}
}
}
return resultList;
}
更加具体的操作 可以去百度看看
LDAP配置
@Configuration
@Data
public class LdapConfig {
@Value("${ldap.username}")
private String userName;
@Value("${ldap.password}")
private String password;
@Value("${ldap.url}")
private String Url;
@Value("${ldap.domain}")
private String domain;
@Value("${ldap.enabled}")
private String enabled;
@Value("${ldap.search-filter}")
private String searchFilter;
}
配置ldapTemplate
@Configuration
public class LdapConfiguration {
@Autowired
private LdapConfig ldapConfig;
@Bean
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
Map<String, Object> config = Maps.newHashMap();
contextSource.setUrl(ldapConfig.getUrl());
contextSource.setBase(rootDnFromDomain(ldapConfig.getDomain()));
contextSource.setUserDn(ldapConfig.getUserName());
contextSource.setPassword(ldapConfig.getPassword());
// 解决乱码
config.put("java.naming.ldap.attributes.binary", "objectGUID");
contextSource.setPooled(true);
contextSource.setBaseEnvironmentProperties(config);
return contextSource;
}
@Bean
public LdapTemplate ldapTemplate() {
LdapTemplate ldapTemplate = new LdapTemplate(contextSource());
//忽略查找结果时出现异常
ldapTemplate.setIgnorePartialResultException(true);
return ldapTemplate;
}
private String rootDnFromDomain(String domain) {
String[] tokens = StringUtils.tokenizeToStringArray(domain, ".");
StringBuilder root = new StringBuilder();
int length = tokens.length;
for (int i = 0; i < length; ++i) {
String token = tokens[i];
if (root.length() > 0) {
root.append(',');
}
root.append("dc=").append(token);
}
return root.toString();
}
}
/**
* 查找用户
* @param name
* @return 用户
*/
@Override
public UserLdap findUser(String name) {
AndFilter filter = new AndFilter();
filter.and(new LikeFilter("sAMAccountName",name));
List<UserLdap> list = ldapTemplate.search("", filter.encode(), new AttributesMapper() {
@Override
public Object mapFromAttributes(Attributes attributes) throws NamingException {
if (attributes != null) {
try {
UserLdap lu = new UserLdap();
for (NamingEnumeration ne = attributes.getAll(); ne.hasMore();) {
// 得到下一个属性
Attribute attr = (Attribute) ne.next();
String id = attr.getID();
String value = attr.get().toString();
if ("displayName".equals(id)){
lu.setUsername(value);
}else if ("sAMAccountName".equals(id)){
lu.setsAMAccountName(value);
}else if ("mobile".equals(id)){
lu.setMobile(value);
}else if ("telephoneNumber".equals(id)){
lu.setTelephoneNumber(value);
}
}
if (lu.getsAMAccountName()!=null){
return lu;
}
return null;
} catch (NamingException e) {
logger.error("LDAP findUser error : ",e);
}
}
return null;
}
});
if (list.size()<1) {
return null;
}
return list.get(0);
}
/**
* 模糊搜索用户
* @param name
* @return 用列表
*/
@Override
public List<UserLdap> searchUsers(String name) {
AndFilter filter = new AndFilter();
filter.and(new LikeFilter("sAMAccountName","*" + name + "*"));
List<UserLdap> list = ldapTemplate.search("", filter.encode(), new AttributesMapper() {
@Override
public Object mapFromAttributes(Attributes attributes) throws NamingException {
if (attributes != null) {
try {
UserLdap lu = new UserLdap();
for (NamingEnumeration ne = attributes.getAll(); ne.hasMore();) {
// 得到下一个属性
Attribute attr = (Attribute) ne.next();
String id = attr.getID();
String value = attr.get().toString();
if ("displayName".equals(id)){
lu.setUsername(value);
}else if ("sAMAccountName".equals(id)){
lu.setsAMAccountName(value);
}else if ("mobile".equals(id)){
lu.setMobile(value);
}else if ("telephoneNumber".equals(id)){
lu.setTelephoneNumber(value);
}
}
if (lu.getsAMAccountName()!=null){
return lu;
}
return null;
} catch (NamingException e) {
logger.error("LDAP searchUsers error : ",e);
}
}
return null;
}
});
if (list.isEmpty()) {
return new ArrayList<>();
}
return list;
}