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 执行绑定操作。
我正在尝试使用 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 执行绑定操作。