You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Data LDAP布尔/日期属性与OpenLDAP语法不兼容问题求助

Spring Data LDAP自定义Schema属性语法兼容问题

问题现象

操作OpenLDAP时抛出属性值语法异常:

javax.naming.directory.InvalidAttributeValueException: [LDAP: error code 21 - XXXActive: value #0 invalid per syntax]

背景信息

  • 自定义Schema配置:
    • XXXActive:采用booleanMatch匹配规则,语法OID为1.3.6.1.4.1.1466.115.121.1.7(布尔类型)
    • XXXQueryFreshnessDate:采用generalizedTimeMatch匹配规则,语法OID为1.3.6.1.4.1.1466.115.121.1.24(通用时间类型)
  • 实体类对应属性:boolean active、Date queryFreshnessDate
  • 问题根源:DefaultObjectDirectoryMapper使用ConversionServiceConverterManager转换时忽略syntax参数,默认将布尔值转为小写"true"/"false"(OpenLDAP要求大写"TRUE"/"FALSE"),日期格式也不符合generalizedTime规范

解决方案

1. 自定义属性转换器

分别针对布尔和日期类型实现符合OpenLDAP语法的转换器:

布尔类型转换器

import org.springframework.core.convert.converter.Converter;
import org.springframework.data.ldap.core.converter.AttributeConverter;

public class LdapBooleanConverter implements Converter<Boolean, String>, AttributeConverter<Boolean> {

    @Override
    public String convert(Boolean source) {
        return source != null ? source.toString().toUpperCase() : null;
    }

    @Override
    public Boolean convert(Object source) {
        if (source == null) {
            return null;
        }
        String value = source.toString().trim();
        return "TRUE".equalsIgnoreCase(value);
    }
}

日期类型转换器(适配generalizedTime语法)

import org.springframework.core.convert.converter.Converter;
import org.springframework.data.ldap.core.converter.AttributeConverter;
import javax.naming.NamingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class LdapGeneralizedTimeConverter implements Converter<Date, String>, AttributeConverter<Date> {

    // generalizedTime标准格式:yyyyMMddHHmmss.SSS'Z',强制使用UTC时区
    private static final SimpleDateFormat GENERALIZED_TIME_FORMAT;

    static {
        GENERALIZED_TIME_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
        GENERALIZED_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
        GENERALIZED_TIME_FORMAT.setLenient(false);
    }

    @Override
    public String convert(Date source) {
        return source != null ? GENERALIZED_TIME_FORMAT.format(source) : null;
    }

    @Override
    public Date convert(Object source) throws NamingException {
        if (source == null) {
            return null;
        }
        try {
            return GENERALIZED_TIME_FORMAT.parse(source.toString().trim());
        } catch (ParseException e) {
            throw new NamingException("解析generalizedTime格式失败: " + source);
        }
    }
}

2. 注册自定义转换器到Spring Data LDAP

通过配置类扩展LDAP配置,替换默认的转换服务:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.ldap.core.LdapTemplate;
import org.springframework.data.ldap.core.convert.LdapConverter;
import org.springframework.data.ldap.core.convert.MappingLdapConverter;
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
import org.springframework.ldap.core.ContextSource;

@Configuration
public class LdapCustomConverterConfig {

    @Bean
    public LdapConverter ldapConverter(LdapMappingContext mappingContext) {
        GenericConversionService conversionService = new GenericConversionService();
        
        // 注册自定义转换器
        conversionService.addConverter(new LdapBooleanConverter());
        conversionService.addConverter(new LdapGeneralizedTimeConverter());
        
        MappingLdapConverter converter = new MappingLdapConverter(mappingContext);
        converter.setConversionService(conversionService);
        return converter;
    }

    @Bean
    public LdapTemplate ldapTemplate(ContextSource contextSource, LdapConverter ldapConverter) {
        LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
        ldapTemplate.setConverter(ldapConverter);
        return ldapTemplate;
    }
}

3. 实体类属性精准绑定(可选)

如果需要针对单个属性指定转换器,可在实体类属性上添加@Attribute注解:

import org.springframework.data.ldap.core.mapping.Attribute;
import org.springframework.data.ldap.core.mapping.Entry;
import javax.naming.Name;
import java.util.Date;

@Entry(base = "ou=users,dc=example,dc=com", objectClasses = {"top", "person", "XXXCustomObjectClass"})
public class User {

    private Name dn;

    @Attribute(name = "XXXActive", converter = LdapBooleanConverter.class)
    private boolean active;

    @Attribute(name = "XXXQueryFreshnessDate", converter = LdapGeneralizedTimeConverter.class)
    private Date queryFreshnessDate;

    // getter、setter方法省略
}

内容的提问来源于stack exchange,提问作者Alexander

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 12:35:24