I have a Spring Boot Webapplication which is working with dao authentication like a charm. But i want to implement active directory authentication too.
For testing purposes I've modified my SecurityConfig like that:
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();
}
}
As it seems, due to adding the activeDirectoryLdapAuthenticationProvider and set the AuthenticationManager to use the activeDirectoryLdapAuthenticationProvider as only provider, my application is using this provider.
Wehn i try to login, my application is throwing the following error:
Caused by: javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090439, comment: AcceptSecurityContext error, data 52e, v4563]
as i've searched the whole internet already (it feels like that), i know that 52e means: Username found but password invalid / wrong.
The problem is, that is not the truth! I've tested my account on several ldap / active directory clients and the account is working 100%!
Hopefully someone is here that can put me in the right direction...
Many Thanks And happy coding...
Sascha
Recently, we had the same error message from one of our Spring Boot applications, which is connected to AD with a very similar setup. The error only occured for one special user, and the password was correct.
We observed that the problem was caused by a special character (German "umlaut") in the password. Changing the password and removing the umlaut fixed the problem.