Java Spring 使用 Kerberos 和 LDAP 的 SSO 授权
Java Spring SSO authorization using Kerberos and LDAP
我正在使用 Java Spring Kerberos 安全库(link)开发基于 Linux 的 Rest API 应用程序。
我已经成功实现了 SSO 身份验证,它按预期工作,但现在需要添加 LDAP 集成以实现基于 ROLE 的授权。
但是,LDAP binding/search 不起作用 - SearchFilter 失败并出现以下异常:
exception
org.springframework.ldap.InvalidSearchFilterException: invalid attribute description; nested exception is javax.naming.directory.InvalidSearchFilterException: invalid attribute description; remaining name 'dc=intranet,dc=example,dc=com'
org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:135)
org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:809)
org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:792)
org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntry(SpringSecurityLdapTemplate.java:194)
org.springframework.security.ldap.search.FilterBasedLdapUserSearch.searchForUser(FilterBasedLdapUserSearch.java:116)
org.springframework.security.ldap.userdetails.LdapUserDetailsService.loadUserByUsername(LdapUserDetailsService.java:38)
org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:66)
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:145)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
root cause
javax.naming.directory.InvalidSearchFilterException: invalid attribute description; remaining name 'dc=intranet,dc=example,dc=com'
com.sun.jndi.ldap.Filter.encodeSimpleFilter(Filter.java:446)
com.sun.jndi.ldap.Filter.encodeFilter(Filter.java:171)
com.sun.jndi.ldap.Filter.encodeFilterString(Filter.java:74)
com.sun.jndi.ldap.LdapClient.search(LdapClient.java:546)
com.sun.jndi.ldap.LdapCtx.doSearch(LdapCtx.java:1985)
com.sun.jndi.ldap.LdapCtx.searchAux(LdapCtx.java:1844)
com.sun.jndi.ldap.LdapCtx.c_search(LdapCtx.java:1769)
com.sun.jndi.ldap.LdapCtx.c_search(LdapCtx.java:1786)
com.sun.jndi.toolkit.ctx.ComponentDirContext.p_search(ComponentDirContext.java:418)
com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:396)
javax.naming.directory.InitialDirContext.search(InitialDirContext.java:297)
org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntryInternal(SpringSecurityLdapTemplate.java:208)
org.springframework.security.ldap.SpringSecurityLdapTemplate.executeWithContext(SpringSecurityLdapTemplate.java:196)
org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:806)
org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:792)
org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntry(SpringSecurityLdapTemplate.java:194)
org.springframework.security.ldap.search.FilterBasedLdapUserSearch.searchForUser(FilterBasedLdapUserSearch.java:116)
org.springframework.security.ldap.userdetails.LdapUserDetailsService.loadUserByUsername(LdapUserDetailsService.java:38)
org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:66)
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:145)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
申请详情:
1.安全-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<context:property-placeholder location="classpath:application.properties"/>
<sec:http entry-point-ref="spnegoEntryPoint" use-expressions="true" >
<sec:intercept-url pattern="/" access="permitAll" />
<sec:intercept-url pattern="/home" access="permitAll" />
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/test" access="authenticated"/>
<sec:intercept-url pattern="/data" access="hasRole('ROLE_ADMIN')"/>
<sec:form-login login-page="/login" />
<sec:custom-filter ref="spnegoAuthenticationProcessingFilter"
before="BASIC_AUTH_FILTER" />
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="kerberosServiceAuthenticationProvider" />
<sec:authentication-provider ref="adAuthenticationProvider" />
</sec:authentication-manager>
<bean id="adAuthenticationProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="${ldap.domain}"/>
<constructor-arg value="${ldap.url}"/>
<property name="userDetailsContextMapper" ref="CustomUserDetailsContextMapper" />
</bean>
<bean id="spnegoEntryPoint"
class="org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint" >
</bean>
<bean id="spnegoAuthenticationProcessingFilter"
class="org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="kerberosServiceAuthenticationProvider"
class="org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider">
<property name="ticketValidator">
<bean
class="org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator">
<property name="servicePrincipal" value="${app.service-principal}" />
<property name="keyTabLocation" value="${app.keytab-location}" />
<property name="debug" value="true" />
</bean>
</property>
<property name="userDetailsService" ref="CustomUserDetailsService" />
</bean>
<bean id="authorizationContextSource" class="org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource">
<constructor-arg value="${ldap.url}" />
<property name="loginConfig">
<bean class="org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig">
<property name="servicePrincipal" value="${app.service-principal}" />
<property name="keyTabLocation" value="${app.keytab-location}" />
<property name="useTicketCache" value="false" />
<property name="isInitiator" value="true" />
<property name="debug" value="true" />
</bean>
</property>
</bean>
<bean id="CustomUserDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
<constructor-arg index="0" ref="userSearch" />
<constructor-arg index="1" ref="CustomLdapAuthoritiesPopulator" />
<property name="userDetailsMapper" ref="CustomUserDetailsContextMapper" />
</bean>
<bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value="${ldap.ldap-search-base}" />
<constructor-arg index="1" value="${ldap.search-filter}" />
<constructor-arg index="2" ref="authorizationContextSource" />
<property name="searchSubtree" value="true" />
</bean>
<bean id="CustomUserDetailsContextMapper" class="com.my.utility.UserDetailsContextMapperImpl" />
<bean id="CustomLdapAuthoritiesPopulator" class="com.my.utility.ActiveDirectoryLdapAuthoritiesPopulator" />
</beans>
2。 application.properties
ldap.url=ldap://intranet.example.com
ldap.domain=intranet.example.com
ldap.ldap-search-base="DC=INTRANET,DC=EXAMPLE,DC=COM"
ldap.search-filter="(userPrincipalName={0})"
app.service-principal=myprincipal@INTRANET.EXAMPLE.COM
app.keytab-location=file:/apps/tomcat/myprincipal.keytab
app.krb5=file:/apps/tomcat/conf/krb5.conf
3。 LDAP DN 值
CN=Full Name,OU=Users,OU=LDN,OU=EMEA,OU=GLB,DC=INTRANET,DC=EXAMPLE,DC=com
CN 值包含全名而不是 user/principal ID (myprincipal@INTRANET.EXAMPLE.COM)。我相信由于这个 LDAP search/binding 没有正常发生:
org.springframework.ldap.InvalidSearchFilterException: invalid attribute description; nested exception is javax.naming.directory.InvalidSearchFilterException: invalid attribute description; remaining name 'dc=intranet,dc=example,dc=com'
问题
能否请您帮助理解问题并修改 CustomUserDetailsService 以纠正此问题?
adAuthenticationProvider 使用登录密码身份验证单独按预期工作,但我不确定如何将其集成到 Kerberos 身份验证管理器中 - kerberosServiceAuthenticationProvider ,它们之间有联系。
kerberosServiceAuthenticationProvider 通过虚拟用户详细信息服务实现按预期工作。但是,只要我用 LDAP 实现替换它,它就会一直失败并显示上述错误消息。
public class DummyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return new User(username, "notUsed", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_USER"));
}
}
感谢您的帮助。
解决方案非常简单 - 删除以下行的 application.properties 配置下的引号:
之前
ldap.ldap-search-base="DC=INTRANET,DC=EXAMPLE,DC=COM"
ldap.search-filter="(userPrincipalName={0})"
之后
ldap.ldap-search-base=DC=INTRANET,DC=EXAMPLE,DC=COM
ldap.search-filter=(userPrincipalName={0})
我正在使用 Java Spring Kerberos 安全库(link)开发基于 Linux 的 Rest API 应用程序。
我已经成功实现了 SSO 身份验证,它按预期工作,但现在需要添加 LDAP 集成以实现基于 ROLE 的授权。
但是,LDAP binding/search 不起作用 - SearchFilter 失败并出现以下异常:
exception
org.springframework.ldap.InvalidSearchFilterException: invalid attribute description; nested exception is javax.naming.directory.InvalidSearchFilterException: invalid attribute description; remaining name 'dc=intranet,dc=example,dc=com'
org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:135)
org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:809)
org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:792)
org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntry(SpringSecurityLdapTemplate.java:194)
org.springframework.security.ldap.search.FilterBasedLdapUserSearch.searchForUser(FilterBasedLdapUserSearch.java:116)
org.springframework.security.ldap.userdetails.LdapUserDetailsService.loadUserByUsername(LdapUserDetailsService.java:38)
org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:66)
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:145)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
root cause
javax.naming.directory.InvalidSearchFilterException: invalid attribute description; remaining name 'dc=intranet,dc=example,dc=com'
com.sun.jndi.ldap.Filter.encodeSimpleFilter(Filter.java:446)
com.sun.jndi.ldap.Filter.encodeFilter(Filter.java:171)
com.sun.jndi.ldap.Filter.encodeFilterString(Filter.java:74)
com.sun.jndi.ldap.LdapClient.search(LdapClient.java:546)
com.sun.jndi.ldap.LdapCtx.doSearch(LdapCtx.java:1985)
com.sun.jndi.ldap.LdapCtx.searchAux(LdapCtx.java:1844)
com.sun.jndi.ldap.LdapCtx.c_search(LdapCtx.java:1769)
com.sun.jndi.ldap.LdapCtx.c_search(LdapCtx.java:1786)
com.sun.jndi.toolkit.ctx.ComponentDirContext.p_search(ComponentDirContext.java:418)
com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:396)
javax.naming.directory.InitialDirContext.search(InitialDirContext.java:297)
org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntryInternal(SpringSecurityLdapTemplate.java:208)
org.springframework.security.ldap.SpringSecurityLdapTemplate.executeWithContext(SpringSecurityLdapTemplate.java:196)
org.springframework.ldap.core.LdapTemplate.executeWithContext(LdapTemplate.java:806)
org.springframework.ldap.core.LdapTemplate.executeReadOnly(LdapTemplate.java:792)
org.springframework.security.ldap.SpringSecurityLdapTemplate.searchForSingleEntry(SpringSecurityLdapTemplate.java:194)
org.springframework.security.ldap.search.FilterBasedLdapUserSearch.searchForUser(FilterBasedLdapUserSearch.java:116)
org.springframework.security.ldap.userdetails.LdapUserDetailsService.loadUserByUsername(LdapUserDetailsService.java:38)
org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:66)
org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:145)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
申请详情:
1.安全-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<context:property-placeholder location="classpath:application.properties"/>
<sec:http entry-point-ref="spnegoEntryPoint" use-expressions="true" >
<sec:intercept-url pattern="/" access="permitAll" />
<sec:intercept-url pattern="/home" access="permitAll" />
<sec:intercept-url pattern="/login" access="permitAll" />
<sec:intercept-url pattern="/test" access="authenticated"/>
<sec:intercept-url pattern="/data" access="hasRole('ROLE_ADMIN')"/>
<sec:form-login login-page="/login" />
<sec:custom-filter ref="spnegoAuthenticationProcessingFilter"
before="BASIC_AUTH_FILTER" />
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="kerberosServiceAuthenticationProvider" />
<sec:authentication-provider ref="adAuthenticationProvider" />
</sec:authentication-manager>
<bean id="adAuthenticationProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="${ldap.domain}"/>
<constructor-arg value="${ldap.url}"/>
<property name="userDetailsContextMapper" ref="CustomUserDetailsContextMapper" />
</bean>
<bean id="spnegoEntryPoint"
class="org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint" >
</bean>
<bean id="spnegoAuthenticationProcessingFilter"
class="org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="kerberosServiceAuthenticationProvider"
class="org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider">
<property name="ticketValidator">
<bean
class="org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator">
<property name="servicePrincipal" value="${app.service-principal}" />
<property name="keyTabLocation" value="${app.keytab-location}" />
<property name="debug" value="true" />
</bean>
</property>
<property name="userDetailsService" ref="CustomUserDetailsService" />
</bean>
<bean id="authorizationContextSource" class="org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource">
<constructor-arg value="${ldap.url}" />
<property name="loginConfig">
<bean class="org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig">
<property name="servicePrincipal" value="${app.service-principal}" />
<property name="keyTabLocation" value="${app.keytab-location}" />
<property name="useTicketCache" value="false" />
<property name="isInitiator" value="true" />
<property name="debug" value="true" />
</bean>
</property>
</bean>
<bean id="CustomUserDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
<constructor-arg index="0" ref="userSearch" />
<constructor-arg index="1" ref="CustomLdapAuthoritiesPopulator" />
<property name="userDetailsMapper" ref="CustomUserDetailsContextMapper" />
</bean>
<bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value="${ldap.ldap-search-base}" />
<constructor-arg index="1" value="${ldap.search-filter}" />
<constructor-arg index="2" ref="authorizationContextSource" />
<property name="searchSubtree" value="true" />
</bean>
<bean id="CustomUserDetailsContextMapper" class="com.my.utility.UserDetailsContextMapperImpl" />
<bean id="CustomLdapAuthoritiesPopulator" class="com.my.utility.ActiveDirectoryLdapAuthoritiesPopulator" />
</beans>
2。 application.properties
ldap.url=ldap://intranet.example.com
ldap.domain=intranet.example.com
ldap.ldap-search-base="DC=INTRANET,DC=EXAMPLE,DC=COM"
ldap.search-filter="(userPrincipalName={0})"
app.service-principal=myprincipal@INTRANET.EXAMPLE.COM
app.keytab-location=file:/apps/tomcat/myprincipal.keytab
app.krb5=file:/apps/tomcat/conf/krb5.conf
3。 LDAP DN 值
CN=Full Name,OU=Users,OU=LDN,OU=EMEA,OU=GLB,DC=INTRANET,DC=EXAMPLE,DC=com
CN 值包含全名而不是 user/principal ID (myprincipal@INTRANET.EXAMPLE.COM)。我相信由于这个 LDAP search/binding 没有正常发生:
org.springframework.ldap.InvalidSearchFilterException: invalid attribute description; nested exception is javax.naming.directory.InvalidSearchFilterException: invalid attribute description; remaining name 'dc=intranet,dc=example,dc=com'
问题
能否请您帮助理解问题并修改 CustomUserDetailsService 以纠正此问题?
adAuthenticationProvider 使用登录密码身份验证单独按预期工作,但我不确定如何将其集成到 Kerberos 身份验证管理器中 - kerberosServiceAuthenticationProvider ,它们之间有联系。
kerberosServiceAuthenticationProvider 通过虚拟用户详细信息服务实现按预期工作。但是,只要我用 LDAP 实现替换它,它就会一直失败并显示上述错误消息。
public class DummyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
return new User(username, "notUsed", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_USER"));
}
}
感谢您的帮助。
解决方案非常简单 - 删除以下行的 application.properties 配置下的引号:
之前
ldap.ldap-search-base="DC=INTRANET,DC=EXAMPLE,DC=COM"
ldap.search-filter="(userPrincipalName={0})"
之后
ldap.ldap-search-base=DC=INTRANET,DC=EXAMPLE,DC=COM
ldap.search-filter=(userPrincipalName={0})