Spring-Ldap

本贴最后更新于 1562 天前,其中的信息可能已经事过景迁

唱歌现在习惯低八度怎么办?( 눈﹏눈 )

背景

  • 经过了这么多年,集团内的各种系统紊乱复杂,结构数据互相同步,而 LDAP 服务器内的组织架构早已没人维护。
  • 现有需求需要无纸化办公的组织架构数据增量同步到 LDAP 服务器中。
  • 简单记录下遇到并解决的问题和方法,万一以后又要维护了呢。

LDAP 基本概念

LDAP 采用目录树的模型,下面是一些概念的解释:

  1. 目录树:在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目。

  2. 条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)。

  3. 对象类:与某个实体类型对应的一组属性,对象类是可以继承的,这样父类的必须属性也会被继承下来。

  4. 属性:描述条目的某个方面的信息,一个属性由一个属性类型和一个或多个属性值组成,属性有必须属性和非必须属性。

名词 全称 说明
dc Domain Component 域名的部分,其格式是将完整的域名分成几部分,如域名为 example.com 变成 dc=example,dc=com(一条记录的所属位置)。
ou Organization Unit 组织单元,组织单元可以包含多种多个对象(entry),如组织单元名为 employee,则 ou=employee,那么 employee 下会有一大堆白给饭桶、精工骨干和划水健将这些实体。
cn Common Name 公共名称,如 hmc999(一条记录的名称),则 cn=hmc999,该条目可以在 ou=employee 下。
sn Surname 一般用来表达姓,如"韩"。
dn Distinguished Name 那上面的例子来说,对于 hmc999 这个实体的定位就是 cn=hmc999,ou=employee,dc=example,dc=com,当然可能还会有国家、其他多级。

依赖引入

SpringProject 已经集成了 LDAP 组件,直接 pom 引入即可。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>

通过使用 Spring LDAP 类 AttributesMapper 和 LdapTemplate 可以很好地减少传统方式的代码量。
首先我们可以通过 AttributesMapper 先构建一个实体,用来收集结果,简单样例如下:

public class DemoAttributeMapper implements AttributesMapper {
/**
 * 将单个Attributes转成单个对象
 * @param attrs
 * @return
 * @throws NamingException
 */
@Override
public Object mapFromAttributes(Attributes attrs) throws javax.naming.NamingException {

    DemoModel demoModel = new DemoModel();

    /**
     * 通过attr
     */
    if (attrs.get(xxx.name()) != null) {
        demoModel.setXxx(attrs.get(xxx.name()).get().toString());
    }
    ...

    return demoModel;
}

编写 config 类,LDAP 服务配置在开发环境的 application-dev.properties 中,通过注解 @Value 注入。

@Configuration
public class LdapConfiguration {

    private final Logger log = LoggerFactory.getLogger(this.getClass());
    
    private LdapTemplate ldapTemplate;
    
    @Value("${spring.ldap.urls}")
    private String url;
    
    @Value("${spring.ldap.base}")
    private String base;
    
    @Value("${spring.ldap.username}")
    private String userName;
    
    @Value("${spring.ldap.password}")
    private String passWord;


    @Bean
    public LdapContextSource contextSource() {
        LdapContextSource contextSource = new LdapContextSource();
        Map<String, Object> config = new LinkedHashMap<>(16);
    
        contextSource.setUrl(url);
        contextSource.setBase(base);
        contextSource.setUserDn(userName);
        contextSource.setPassword(passWord);
    
        //  解决乱码的关键一句
        config.put("java.naming.ldap.attributes.binary", "objectGUID");
    
        contextSource.setPooled(true);
        contextSource.setBaseEnvironmentProperties(config);
        log.info(" [ MatthewHan ] : LDAP-Config启动 ");
        return contextSource;
    }
    
    @Bean
    public LdapTemplate ldapTemplate() {
        if (null == ldapTemplate) {
            ldapTemplate = new LdapTemplate(contextSource());
        }
        return ldapTemplate;
    }
}

需要注意的问题

接着就是 Ldaptemplate 的方法用来增删改查,详细的懒得展开了,有几个点需要注意下:

  • Ldaptemplate 需要动态生成 DN 便于增删改查,因为很多操作都是基于构建 baseDN,自己需要更是实际业务情况,编写工具类用于 baseDN 的生成。
  • 更新操作方法 rebind(),其实是先解绑当前实体,在重新绑定,如果该实体存在下级实体,就会 throw 错误,所以在不确定条目结构时,可以使用 ModifyAttributes 类来处理。
  • 因为 LDAP 是树状结构模型,所以在绑定与解绑过程中,一定要注意它是否存在上下级关系,比如在 ou=department 下存在顶级部门、次级部门和下级部门,deId=003001deId=003 的子部门,如果要解绑整个部门(包括子部门)那么就不能使用 unbind() 直接解绑 deId=003 这个实体,但是可以先解绑最下级部门,再一级一级往上解绑。绑定也是一样的原理,先绑定最上级部门,然后再绑定其下级、最下级部门。题外话:讲道理应该有可以直接解绑该实体以及他的全部子实体吧!但是我好像没找到。。
  • 遗留的一个问题,害怕会在项目中会引发墨菲定律的风险,已知一个实体的一个属性 deId=003001,包括 ou=employee,他的实际 DN 为 deId=003001,deId=003,ou=department...,从 DN 中我们知道他是属于 deId=003 的子实体,但是可以通过配置 AndFilter 配置过滤器,从而使用 Ldaptemplatesearch 方法去定位该实体,却不知道他的 DN。。我在 Ldaptemplate 中未找到一个 method,这是个未解决的问题。关于 LDAP 的内容,整个互联网上也不是太多,社区也不够活跃,希望以后能够知道答案。

相关帖子

回帖

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...