Spring Security Active Directory Authentication - infinite login pop-up - spring

I am using Spring Boot (2.7.2) security. My security config is:
public class WebSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().httpBasic();
return http.build();
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(
"company.com", "ldap://ldap-company.com:389");
provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={0}))");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
Now when I hit my URI I keep getting the login pop-up infinitely.
The username and password I am providing is correct. No error(s) at the console whatsoever.
What am I doing wrong here or missing?

While I am still waiting for the right answer, I got the idea from here and it works.
So this is what I ended up with:
public class WebSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
// .fullyAuthenticated()
.and().httpBasic();
return http.build();
}
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://ldap-company.com:389/dc=company,dc=com");
contextSource.setUserDn("CN=MYBindUser,OU=Ldap,dc=COMPANY,dc=com");
contextSource.setPassword("ComplexP#ssw0rd");
contextSource.setReferral("follow");
contextSource.afterPropertiesSet();
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth
.ldapAuthentication();
ldapAuthenticationProviderConfigurer
.userSearchFilter("(&(cn={0}))")
// .userSearchFilter("(sAMAccountName=%s)")
.userSearchBase("")
// .groupSearchBase("(&(objectCategory=group)(cn={0}))")
.contextSource(contextSource);
}
}
Now my HTTPBasic Authentication with ActiveDirectory LDAP works just fine.

Related

spring boot API + security +ldap

I want to achieve LDAP verification without form login, But not sure how to achieve it.
EDITED: I have a custom POST API /login which accept userId and password and validate it and creates a session cookies/Token.
Few doubts:
how to point to custom login URL endpoint?
How internally the form login used to validate the cookie once we successfully validated the credentials? How to achieve the same with custom login endpoint?
Do i have to change something in CRSF?
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LdapProperties ldapProperties;
#Bean
public LdapAuthenticationProvider
authenticationProvider(final LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
#Bean
public BindAuthenticator authenticator(final BaseLdapPathContextSource contextSource) {
final BindAuthenticator authenticator = new BindAuthenticator(contextSource);
authenticator.setUserDnPatterns(new String[] {
ldapProperties.getUserDnPatterns() });
return authenticator;
}
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userSearchFilter(ldapProperties.getSearchFilter())
.contextSource(contextSource());
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.formLogin().and()
.authorizeRequests()
.anyRequest().fullyAuthenticated().and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login");
}
#Override
public void configure(final WebSecurity web) throws Exception {
web.debug(true);
}
#Bean
public LdapContextSource contextSource() {
final LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(ldapProperties.getUrl());
contextSource.setBase(ldapProperties.getBase());
contextSource.setUserDn(ldapProperties.getUserDn());
contextSource.setPassword(ldapProperties.getUserDnPaswrd());
contextSource.setPooled(true);
contextSource.afterPropertiesSet();
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate() {
final LdapTemplate ldapTemplate = new LdapTemplate(
contextSource());
ldapTemplate.setIgnorePartialResultException(true);
return ldapTemplate;
}
}

Spring boot + LDAP form login, logout and validating the token after login

I am integrating spring boot with LDAP to Authenticate a user. So that only authenticated users can only access the API.
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
LdapAuthenticationProvider authenticationProvider(final LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
#Bean
BindAuthenticator authenticator(final BaseLdapPathContextSource contextSource) {
final BindAuthenticator authenticator = new BindAuthenticator(contextSource);
authenticator.setUserDnPatterns(new String[] {
"xx" });
return authenticator;
}
#Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userSearchFilter("(sAMAccountName={0})")
.contextSource(contextSource());
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.formLogin().and()
.authorizeRequests()
.anyRequest().fullyAuthenticated().and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login");
}
#Override
public void configure(final WebSecurity web) throws Exception {
web.debug(true);
}
#Bean
LdapContextSource contextSource() {
final LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl("xx);
contextSource.setBase("xx");
contextSource.setUserDn("xx");
contextSource.setPassword("xx");
contextSource.setPooled(true);
contextSource.afterPropertiesSet();
return contextSource;
}
#Bean
public LdapTemplate ldapTemplate() {
final LdapTemplate ldapTemplate = new LdapTemplate(
contextSource());
ldapTemplate.setIgnorePartialResultException(true);
return ldapTemplate;
}
}
I am using the inbuild form login.
Question
Who (which class) is responsible to create a success token and where is it stored and in successive calls how is it validated?
Now I am only redirecting the unauthenticated calls to the login page due to this it giving 200 success responses, How to override this and send 401
Edited:
I have one specific question
If there is no token, the user is stored in the session -> how subsequent requests are validated. Which all classes are used

Unable to use Session Management with Spring Boot

I have declared the following functions in an open source core banking solution based on Spring boot (Fineract) to limit the number of concurrent sessions per user to 1. My WebSecurity.java file is as follows:
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry());
}
// Work around https://jira.spring.io/browse/SEC-2855
#Bean
public SessionRegistry sessionRegistry() {
SessionRegistry sessionRegistry = new SessionRegistryImpl();
return sessionRegistry;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("mifos").password("password").roles("USER");
}
// Register HttpSessionEventPublisher
#Bean
public static ServletListenerRegistrationBean httpSessionEventPublisher() {
return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
}
}
The SecurityWebApplicationInitializer.java is as follows:
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class };
}
}
However, I am still able to log into the system with multiple private browser windows opened. My assumption is that the problem is either with the SpringSecurityFilterChain not being registered with war, or with the way I am chaining the functions of the HttpSecurity object. Since I did not declare a customized login form or have defined any expired URL pages, I had to edit the steps shown in the following link: https://github.com/spring-projects/spring-boot/issues/1537 . Any leads on how to diagnose this issue? Thanks in advance.

Spring - Add a check for 3rd parameter during authentication

At a glance, I have API Back-end App written in Spring Boot which uses JWT for secured data transmission. I want to add 3rd parameter for authorization, so I should have login, password and storeID parameters. I am inspired by this answer How implement Spring security when login page having more field apart from user name and password? but when I followed proposed solution my 3rd parameter in not used. My impression is that I am missing something important in Security Config. Could you please point to my mistake?
SecurityConfig
#SuppressWarnings("SpringJavaAutowiringInspection")
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationDetailsSource<HttpServletRequest, ?> webAuthenticationDetailsSourceImpl;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.authenticationProvider(myAuthProvider());
}
#Bean
public CustomUserDetailsAuthenticationProvider myAuthProvider() throws Exception {
CustomUserDetailsAuthenticationProvider provider = new CustomUserDetailsAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(userDetailsService);
return provider;
}
#Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter();
usernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager());
usernamePasswordAuthenticationFilter.setAuthenticationDetailsSource(webAuthenticationDetailsSourceImpl);
return usernamePasswordAuthenticationFilter;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationTokenFilter authenticationTokenFilter = new JwtAuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// we don't need CSRF because our token is invulnerable
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
// allow anonymous resource requests
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// disable page caching
httpSecurity.headers().cacheControl();
}
}
I was under impression I can check against storeID field in WebAuthenticationDetailsSourceImpl, but looks like it has never been executed because I don't see anything related in log.
WebAuthenticationDetailsSourceImpl:
#Component
public class WebAuthenticationDetailsSourceImpl implements AuthenticationDetailsSource<HttpServletRequest, JwtAuthenticationRequest> {
#Override
public JwtAuthenticationRequest buildDetails(HttpServletRequest context) {
System.out.println("___#####_____");
System.out.println(context);
System.out.println("___#####_____");
return new JwtAuthenticationRequest();
}
}
cuz you don't insert "your" usernamePasswordAuthenticationFilter that set webAuthenticationDetailsSourceImpl to Spring Security's authentication filter chain.
perhaps current your authentication filter chain is
~
JwtAuthenticationTokenFilter
(Spring Security's original)UsernamePasswordAuthenticationFilter
~
hence,if you want to retrieve your additional parameter in "your" usernamePasswordAuthenticationFilter add this filter too like a JwtAuthenticationTokenFilter
but , if you want to simply retrieve parameter at JwtAuthenticationTokenFilter
use setAuthenticationDetailsSource at there
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationTokenFilter authenticationTokenFilter = new JwtAuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
authenticationTokenFilter.setAuthenticationDetailsSource(webAuthenticationDetailsSourceImpl);
return authenticationTokenFilter;
}

Spring Boot LDAP Authentication: Always get bad credentials

I'm trying to authenticate with a Spring Boot application against an Active Directory server in my local network, but I don't know what could I be doing wrong.
When I access localhost I am redirected to the login page:
Whenever I write any real user credentials, I'm redirected to the same page with an error message:
If I send a random word as user and password I get the same login error screen, but additionaly this message is shown from Eclipse console:
2016-02-04 18:54:47.591 INFO 10092 --- [nio-8080-exec-8] ctiveDirectoryLdapAuthenticationProvider : Active Directory authentication failed: Supplied password was invalid
From the Active Directory Server, the distinguishedName of the group that I want to access is: CN=Bulnes,OU=Usuarios Locales,DC=Bulnes,DC=local, so it is configured in security configuration class like this:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.formLogin();
}
#Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
ActiveDirectoryLdapAuthenticationProvider provider=
new ActiveDirectoryLdapAuthenticationProvider("bulnes.local"
,"ldap://192.168.1.3:389/"
,"CN=Bulnes,OU=Usuarios Locales,DC=Bulnes,DC=local");
auth.authenticationProvider(provider);
}
}
}
This is how I have it working:
ad.properties
ad.url=ldap://yourserver.abc.com:389
ad.domain=abc.com
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${ad.domain}")
private String adDomain;
#Value("${ad.url}")
private String adUrl;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login", "/css/**", "/public/**").permitAll().anyRequest().authenticated()
.and().formLogin().loginPage("/login").defaultSuccessUrl("/", true)
.failureUrl("/login?failed=badcredentials")
.permitAll().and().logout().logoutUrl("/logout")
.logoutSuccessUrl("/login");
}
#Bean
#Override
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(adDomain,
adUrl);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
Just created the provider like this, and it works fine.
ActiveDirectoryLdapAuthenticationProvider provider=
new ActiveDirectoryLdapAuthenticationProvider("bulnes.local"
,"ldap://192.168.1.3:389);
It still gives an exception but at least authenticates
2016-02-04 21:30:36.293 INFO 12056 --- [nio-8080-exec-3] o.s.s.ldap.SpringSecurityLdapTemplate : Ignoring PartialResultException

Resources