我有一个 Spring Boot Web 应用程序,它像魅力一样使用 dao 身份验证。但我也想实现活动目录身份验证。
为了测试目的,我修改了我的 SecurityConfig,如下所示:
package com.sheimann.demoapp.config;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
import org.springframework.security.web.SecurityFilterChain;
import com.sheimann.demoapp.service.UserServiceImpl;
/**
* The Class WebSecurityConfig.
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
/** The success handler. */
@Autowired
private CustomLoginSucessHandler sucessHandler;
@Autowired
private CustomLoginFailureHandler failureHandler;
/**
* User details service.
*
* @return the user details service
*/
@Bean
UserDetailsService userDetailsService() {
return new UserServiceImpl();
}
/**
* Password encoder.
*
* @return the b crypt password encoder
*/
@Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* Authentication provider.
*
* @return the dao authentication provider
*/
@Bean
DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider =
new ActiveDirectoryLdapAuthenticationProvider( "mydomainName.com", "ldap://mydcipaddress:389", "dc=my,dc=domain,dc=com");
// to parse AD failed credentails error message due to account - expiry,lock, credentialis - expiry,lock
activeDirectoryLdapAuthenticationProvider.setConvertSubErrorCodesToExceptions(true);
activeDirectoryLdapAuthenticationProvider.setUseAuthenticationRequestCredentials(true);
activeDirectoryLdapAuthenticationProvider.setSearchFilter("(&(objectClass=user)(sAMAccountName={0}))");
return activeDirectoryLdapAuthenticationProvider;
}
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(List.of(activeDirectoryLdapAuthenticationProvider()));
}
/**
* Filter chain.
*
* @param http the http
* @return the security filter chain
* @throws Exception the exception
*/
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
// URL matching for accessibility
.requestMatchers("/datatables/**",
"/css/**",
"/js/**",
"/webjars/**",
"/app.js",
"/app.css",
"/just-validate/**",
"/bootstrap-icons/**",
"/bootstrap/**",
"/popperjs/**",
"/select2-bootstrap-5-theme-1.3.0/**",
"/select2-4.1.0-rc.0/**",
"/jquery/**",
"/*.p12",
"/login",
"/register",
"/resetPw1",
"/resetPw2",
"/error",
"/logout",
"/images/**")
.permitAll()
.requestMatchers("/admin/**").hasAnyAuthority("ADMIN")
.requestMatchers("/user/**").authenticated()
.anyRequest().authenticated()
.and()
// form login
.csrf((csrf) -> csrf.disable())
.formLogin(login -> login
.loginPage("/login")
.failureHandler(failureHandler)
.successHandler(sucessHandler)
.usernameParameter("email")
.passwordParameter("password"))
// logout
.logout(logout -> logout
.logoutSuccessUrl("/login?res=logoutSuccess"))
.exceptionHandling(handling -> handling
.accessDeniedPage("/access-denied"))
.authenticationManager(authenticationManager());
// session
/*
* .and()
* .sessionManagement()
* .invalidSessionUrl("/login?res=sst");
*/
//http.authenticationProvider(authenticationProvider());
http.headers(headers -> headers.frameOptions().sameOrigin());
return http.build();
}
}
看起来,由于添加了 activeDirectoryLdapAuthenticationProvider 并将 AuthenticationManager 设置为使用 activeDirectoryLdapAuthenticationProvider 作为唯一提供程序,我的应用程序正在使用此提供程序。
当我尝试登录时,我的应用程序抛出以下错误:
Caused by: javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090439, comment: AcceptSecurityContext error, data 52e, v4563]
因为我已经搜索了整个互联网(感觉就是这样),我知道 52e 的意思是:用户名找到,但密码无效/错误。
问题是,事实并非如此!我已经在多个 LDAP / Active Directory 客户端上测试了我的帐户,该帐户运行 100%!
希望有人可以引导我走向正确的方向......
非常感谢,编码愉快...
萨沙
最近,我们的 Spring Boot 应用程序之一收到了相同的错误消息,该应用程序通过非常相似的设置连接到 AD。该错误仅发生在一位特殊用户身上,并且密码是正确的。
我们观察到该问题是由密码中的特殊字符(德语“umlaut”)引起的。更改密码并删除变音符号解决了问题。