Spring 启动 2 + Microsoft AD 身份验证 + 未找到用户密码属性

Spring boot 2 + Microsoft AD authentication + userPassword attribute not found

我正在尝试使用 spring boot 2.1.9.RELEASE 针对 Microsoft AD 对用户进行身份验证。我能够从 AD 中找到用户 dn,但它没有对用户进行身份验证并出现空指针异常。

我的 WebSecurityConfig 代码

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        // auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());

        auth.ldapAuthentication().userDnPatterns("CN={0},OU=Users")
                .groupSearchBase("ou=groups")
                .userSearchFilter("(sAMAccountName={0})").contextSource()
                .url(AD_URL)
                .managerDn(AD_USERNAME).managerPassword(AD_PASSWORD).and()
                .passwordCompare()
                .passwordEncoder(new LdapShaPasswordEncoder())
                .passwordAttribute("userPassword");

    }

错误日志

w.a.UsernamePasswordAuthenticationFilter : Request is to process authentication
o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.ldap.authentication.LdapAuthenticationProvider
o.s.s.l.a.LdapAuthenticationProvider     : Processing authentication request for user: <username>
o.s.l.c.support.AbstractContextSource    : Got Ldap context on server 'ldap://<companyname>.com:389/dc=<companyname>,dc=com'
o.s.s.l.s.FilterBasedLdapUserSearch      : Searching for user '<username>', with user search [ searchFilter: '(sAMAccountName={0})', searchBase: '', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
o.s.l.c.support.AbstractContextSource    : Got Ldap context on server 'ldap://<companyname>.com:389/dc=<companyname>,dc=com'
o.s.s.ldap.SpringSecurityLdapTemplate    : Searching for entry under DN 'dc=<companyname>,dc=com', base = '', filter = '(sAMAccountName={0})'
o.s.s.ldap.SpringSecurityLdapTemplate    : Found DN: CN=<User Full Name>,OU=Users,OU=HM Bangalore,OU=Office Locations
o.s.s.ldap.SpringSecurityLdapTemplate    : Ignoring PartialResultException
.s.s.l.a.PasswordComparisonAuthenticator : Performing LDAP compare of password attribute 'userPassword' for user 'CN=<User Full Name>,OU=Users,OU=HM Bangalore,OU=Office Locations'
o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@456cde20
w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [/api] threw exception

java.lang.NullPointerException: null
    at org.springframework.security.crypto.password.LdapShaPasswordEncoder.extractPrefix(LdapShaPasswordEncoder.java:193) ~[spring-security-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.crypto.password.LdapShaPasswordEncoder.matches(LdapShaPasswordEncoder.java:162) ~[spring-security-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.crypto.password.LdapShaPasswordEncoder.matches(LdapShaPasswordEncoder.java:158) ~[spring-security-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator.isPasswordAttrCompare(PasswordComparisonAuthenticator.java:121) ~[spring-security-ldap-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator.authenticate(PasswordComparisonAuthenticator.java:109) ~[spring-security-ldap-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:187) ~[spring-security-ldap-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:85) ~[spring-security-ldap-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175) ~[spring-security-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:200) ~[spring-security-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
.
.
.

进一步调查错误日志,我发现下面的代码试图从用户对象中获取编码后的密码,但我的 passwordAttributeName "userPassword" 不在用户对象中

org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator.isPasswordAttrCompare(PasswordComparisonAuthenticator.java:121)

.
.
.
    private boolean isPasswordAttrCompare(DirContextOperations user, String password) {
        String passwordAttrValue = getPassword(user);
        return passwordEncoder.matches(password, passwordAttrValue);
    }

    private String getPassword(DirContextOperations user) {
        Object passwordAttrValue = user.getObjectAttribute(this.passwordAttributeName);
        if (passwordAttrValue == null) {
            return null;
        }
        if (passwordAttrValue instanceof byte[]) {
            return new String((byte[]) passwordAttrValue);
        }
        return String.valueOf(passwordAttrValue);
    }
.
.
.

不确定为什么我无法从 AD 获取编码密码。 任何帮助将不胜感激。

Microsoft Active Directory 不使用普通的 "userPassword" 密码,而是使用“unicodePwd”。

此外,与许多 LDAP 服务器实施一样,Microsoft Active Directory 不会 return 密码属性的值。

最后,对密码执行 LDAP 比较是一种不良做法,不应使用,因为某些内置功能(例如密码过期和入侵者检测)可能会在以下情况下被绕过对 userPassword 属性执行比较请求。 您应该始终对 LDAP 执行绑定操作。