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
相关产品推荐
相关产品推荐

