Spring Security: Configuring Authentication for Multiple User Types with Custom UserDetailsService And Distinct Success pages

Ahmed Ajmine Nehal
3 min readNov 15, 2023

--

Spring security poster

Spring Security is a Java framework that provides authentication, authorization, and other security features for enterprise applications. In Spring Security, managing authentication for diverse user types is a common challenge. This article will discuss configuring Spring Security for multiple user types while utilizing a single Authentication Manager.

Configuring Multiple User Details Service:

Here, we have configured two userDetailsService classes: one is for USER1, and one is for USER2. Assuming both are different types of users.

UserDetailsServiceFor_User1 class

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.stream.Collectors;

public class UserDetailsServiceFor_User1 implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

}
}

UserDetailsServiceFor_User2 class

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.stream.Collectors;

public class UserDetailsServiceFor_User1 implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

}
}

Configuring Authentication Success Handler:

Authentication Success Handler class redirects users to a success page after successful authentication. Different user types may need different success pages. For that, we can configure the Authentication Success Handler class, which will redirect each user type to their own success page.

AuthenticationSuccessHandlerClass class

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;


@Component
public class AuthenticationSuccessHandlerClass implements AuthenticationSuccessHandler {

@Autowired
private User1_Service user1Service;

@Autowired
private User2Service user2Service;

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
String targetUrl = determineTargetUrl(authentication);

if (response.isCommitted()) {
return;
}

response.sendRedirect(targetUrl);
}

private String determineTargetUrl(Authentication authentication) {

String user = getUser(authentication);

System.out.println(user);

if ("USER_1".equals(user)) {
return "/user1/user1_success_page";
} else if ("USER_2".equals(user)) {
return "/user2/user2_success_page";
}

return "/";
}

private String getUser(Authentication authentication) {

String temp = null;
String username = authentication.getName();

User1 user_1 = user1Service.findByUsername(username);
User2 user_2 = user2Service.findByUsername(username);

if (user_1 != null) {
temp = "USER_1";
}else if(user_2 != null){
temp = "USER_2";
}

return temp;
}
}

Here, we have configured the Authentication Success Handler Class.

Configuring Authentication Manager:

Authentication manager is for handling user authentication. Managing multiple user types introduces a challenge. Different user types may require distinct authentication logic, such as varied user details services, authentication rules, or success pages.

SecurityConfiguration class

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

@Autowired
AuthenticationSuccessHandlerClass authenticationSuccessHandlerClass;

@Bean
public UserDetailsService userDetailsService_User1() {
return new UserDetailsServiceFor_User1();
}

@Bean
public UserDetailsService userDetailsService_User2() {
return new UserDetailsServiceFor_User2();
}

@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public SecurityFilterChain filterChainAdmin(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http
.getSharedObject(AuthenticationManagerBuilder.class);

authenticationManagerBuilder
.userDetailsService(userDetailsService_User1())
.passwordEncoder(passwordEncoder());

authenticationManagerBuilder
.userDetailsService(userDetailsService_User2())
.passwordEncoder(passwordEncoder());

AuthenticationManager authenticationManager = authenticationManagerBuilder.build();

http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(author -> author
.requestMatchers(PathRequest.toStaticResources().atCommonLocations())
.permitAll()
.requestMatchers("/**", "/js/**", "/css/**", "/images/**",
"/webfonts/**")
.permitAll()
.requestMatchers("/USER1/**").hasAuthority("USER1")
.requestMatchers("/USER2/**").hasAuthority("USER2")
.permitAll()
.anyRequest().authenticated())
.formLogin(login -> login.loginPage("/login")
.loginProcessingUrl("/do-login")
.successHandler(authenticationSuccessHandlerClass)
.permitAll())
.logout(logout -> logout.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.permitAll())
.authenticationManager(authenticationManager)
.sessionManagement(
session -> session.sessionCreationPolicy(SessionCreationPolicy.ALWAYS));
return http.build();
}

}

Here, we have configured Authentication Manager for multiple User Details Services for multiple types of users and added configuration for the Authentication Success Handler class in the SecurityConfiguration class.

In this article, we have configured the UserDetailsService, AuthenticationSuccessHandler, and AuthenticationManager to manage the authentication process for diverse user types within a unified authentication and security configuration.

--

--

No responses yet