一、什么是 LDAP
LDAP 是轻量级目录拜访协定的简称。
目录是一个为查问、浏览和搜寻而优化的数据库,它成树状构造组织数据,相似文件目录一样。
二、为什么要用 LDAP
咱们利用 LDAP 要达到的最终目标就是实现对立登录,比方咱们有多个零碎,每个零碎都是独立运行,然而咱们想要为了方便管理想要把这些用户全存在一起并且这些零碎登录时对立向这个库进行查问。
这时候咱们就用到了 LDAP,那么
三、用 docker 部署 LDAP 和 LDAP 可视化界面
1、装置 openldap
docker run \
-d \
-p 389:389 \
-p 636:636 \
-v /usr/local/ldap:/usr/local/ldap \
-v /data/openldap/ldap:/var/lib/ldap \
-v /data/openldap/slapd.d:/etc/ldap/slapd.d \
--env LDAP_ORGANISATION="imysh" \
--env LDAP_DOMAIN="imysh.com" \
--env LDAP_ADMIN_PASSWORD="123456" \
--name openldap \
--hostname openldap-host\
--network bridge \
osixia/openldap
其中 -p 389:389 \ TCP/IP 拜访端口
-p 636:636 \ SSL 连贯端口。
–network bridge 连贯默认的 bridge 网络(docker0)
–hostname openldap-host 设置容器主机名称为 openldap-host
–env LDAP_ORGANISATION=“imysh”配置 LDAP 组织名称
–env LDAP_DOMAIN=“imysh.com”配置 LDAP 域名
–env LDAP_ADMIN_PASSWORD=“123456”配置 LDAP 明码
默认登录用户名:admin
2、装置可视化界面 phpidapadmin
docker run \
-p 8081:80 \
--privileged \
--name phpldapadmin \
--env PHPLDAPADMIN_HTTPS=false \
--env PHPLDAPADMIN_LDAP_HOSTS=192.168.x.xxx \
--detach osixia/phpldapadmin
其中 PHPLDAPADMIN_LDAP_HOSTS 为配置 openLDAP 的 IP 或域名,咱们是在本地起的,所以填写咱们本机 IP 即可。
查问本机 IP:ifconfig | grep "inet"
存储形式
不像咱们熟知的关系数据库,数据都在表中,一行一行的,高深莫测,这个 OpenLDAP 是以树的形式存储的。比方一个人的信息是这样的:
名词解释:
dc: 域组织(能够把它当作关系型中的库)
比方将 http://liming.tcom 这样的域名,能够拆成 dc=liming,dc=com 这样的模式。
ou: 指代组织单元、部门
cn: 通用名称,个别为用户名
dn:用来惟一标识一项的门路。(一条 dn 就相似于关系型数据库中的一条数据)
例:
比方这个张三的 dn:cn=zhang san,cn=newgroup,dc=imysh,dc=com
拜访治理首页
拜访: 127.0.0.1:8081
用咱们初始化的管理员账号登录
账号:cn=admin,dc=imysh,dc=com
明码:123456
新建部门、组和用户
新建部门(ou):
新建组(cn)
新建用户
咱们能够在新建用户时配置他的姓氏名称以及所属组、用户名、明码、加密形式等信息。
四、SpringBoot 集成 LDAP
所用依赖:
<dependency>
<groupId>com.sun</groupId>
<artifactId>ldapbp</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>compile</scope>
</dependency>
配置:
/**
* LDAP 的主动配置类
*
* 实现连贯 及 LdapTemplate 生成
*/
@Configuration
public class LdapConfiguration {
private LdapTemplate ldapTemplate;
@Bean
public LdapContextSource contextSource() {LdapContextSource contextSource = new LdapContextSource();
Map<String, Object> config = new HashMap();
contextSource.setUrl(LdapConstans.url);
contextSource.setBase(LdapConstans.BASE_DC);
contextSource.setUserDn(LdapConstans.username);
contextSource.setPassword(LdapConstans.password);
// 解决 乱码 的要害一句
config.put("java.naming.ldap.attributes.binary", "objectGUID");
contextSource.setPooled(true);
contextSource.setBaseEnvironmentProperties(config);
return contextSource;
}
@Bean
public LdapTemplate ldapTemplate() {if (null == ldapTemplate)
ldapTemplate = new LdapTemplate(contextSource());
return ldapTemplate;
}
}
通过下面的代码,在 IOC 容器实现 bean 的定义,咱们在内部就能够注入应用 LdapTemplate 了
LdapTemplate 实现 CRUD 性能:
建设 person 类:
@Data
@Entry(base = "cn=group,ou=department", objectClasses="inetOrgPerson")
public class Person {
/**
* 此字段不能少
*/
@Id
private Name id;
@DnAttribute(value = "uid", index = 3)
private String uid;
@Attribute(name = "cn")
private String commonName;
@Attribute(name = "sn")
private String myUerName;
private String userPassword;
}
新增 person
@Test
public void addPerson() {Person person = new Person();
person.setUid("uid:15");
person.setMyUerName("liMing");
person.setCommonName("liming");
person.setUserPassword("123456");
ldapTemplate.create(person);
}
后果:
查问 person
@Test
public void getUsers() {String filter = "(&(objectclass=inetOrgPerson))";
List<Person> list = ldapTemplate.search("cn=group,ou=department", filter, new AttributesMapper() {
@SneakyThrows
@Override
public Object mapFromAttributes(Attributes attributes) throws NamingException {
// 能够通过上面办法打印属性
NamingEnumeration<? extends Attribute> att = attributes.getAll();
while (att.hasMore()) {Attribute a = att.next();
System.out.println(a.getID() + "=" + a.get());
}
Person user = new Person();
Attribute a = attributes.get("sn");
if (a != null) user.setMyUerName((String)a.get());
a = attributes.get("userPassword");
if (a != null) user.setUserPassword(a.get().toString());
a = attributes.get("cn");
if (a != null) user.setCommonName((String) a.get());
return user;
}
});
System.out.println(list);
}
对于其中的 filter 要留神 filter 整体要在括号里,否则会报如下谬误:
org.springframework.ldap.InvalidSearchFilterException: invalid attribute description; nested exception is javax.naming.directory.InvalidSearchFilterException: invalid attribute description; remaining name 'cn=group,ou=department'
- 获取域列表, 如果要获取确定的某一个域,filter 能够这样写: (&(objectclass=dcObject)&(dc=imysh))
- 获取组织列表, 如果要获取确定的某一个组织,filter 能够这样写: (&(objectclass=organizationalUnit)&(ou=people)
- 获取 people 列表, 如果要获取某一个确定的人,filter 能够这样写: (&(objectclass=inetOrgPerson)&(uid=uid:13))
后果:
更新
@Test
public void update(){
String oldPersonDn = "uid=uid:15,cn=group,ou=department";
Person newPerson = new Person();
newPerson.setMyUerName("newLiming");
newPerson.setCommonName("newCommentName");
ldapTemplate.modifyAttributes(oldPersonDn, new ModificationItem[] {new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("cn", newPerson.getCommonName().trim())),
new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("sn", newPerson.getMyUerName().trim())),
});
}
更新就是替换属性,应用 ModificationItem 类进行解决。
就像咱们之前应用的数据库一样,最简略的就是通过 ID 来进行更新,也就相当于这里的 dn
后果:
删除
@Test
public void delete() {
String PersonDn = "uid=uid:15,cn=group,ou=department";
ldapTemplate.unbind(PersonDn);
}
后果:
删除也就是解绑的过程,间接调用 unbind 即可