While working with spring security I had a look at interesting thread in stackoverflow, there it was requirement to have authenticating two set of users against different authentication provider say employees against LDAP
and customer against DATABASE
. Thread came up with accepted solution to have a single login form with a radio button to distinguish employee from customer and to have custom authentication filter which differentiate login request based on userType and sets different authenticationToken(customerAuthToken/employeeAuthToken) and request is proceeded for authentication. There will be two AuthenticationProvider
implementation and authentication is done and decided by supporting token.
In this way thread was able to provide interesting solution to avoid fallback authentication which spring security provides by default.
Have a look at thread Configuring Spring Security 3.x to have multiple entry points
Since answer is completely in xml configuration. I just wanted to have the solution be available in java configuration. I will be posting that in answer.
Now my question, with evolution of spring version, is it possible to have the same functionality by any new features/ minimal configurations apart from my answer?
Since this thread given complete information, i am just posting codes for java configuration reference.
Here i am assuming following things
1. User's and Admin's as two set of users.
2. For simplicity using in memory authentication for both.
- If userType is User only user credential should work.
- If userType is Admin only admin credential should work.
- And should be able to provide same application interface with different authorities.
And the codes
You can download working code from my github repository
CustomAuthenticationFilter
@Component
public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException
{
UsernamePasswordAuthenticationToken authToken = null;
if ("user".equals(request.getParameter("userType")))
{
authToken = new UserUsernamePasswordAuthenticationToken(request.getParameter("userName"), request.getParameter("password"));
}
else
{
authToken = new AdminUsernamePasswordAuthenticationToken(request.getParameter("userName"), request.getParameter("password"));
}
setDetails(request, authToken);
return super.getAuthenticationManager().authenticate(authToken);
}
}
CustomAuthentictionTokens
public class AdminUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken
{
public AdminUsernamePasswordAuthenticationToken(Object principal, Object credentials)
{
super(principal, credentials);
}
public AdminUsernamePasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities)
{
super(principal, credentials, authorities);
}
}
public class UserUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken
{
public UserUsernamePasswordAuthenticationToken(Object principal, Object credentials)
{
super(principal, credentials);
}
public UserUsernamePasswordAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities)
{
super(principal, credentials, authorities);
}}
CustomAuthentictionProvider - For Admin
@Component
public class AdminCustomAuthenticationProvider implements AuthenticationProvider
{
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
String username = authentication.getName();
String password = authentication.getCredentials().toString();
if (username.equals("admin") && password.equals("admin@123#"))
{
List<GrantedAuthority> authorityList = new ArrayList<>();
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_ADMIN");
authorityList.add(authority);
return new UserUsernamePasswordAuthenticationToken(username, password, authorityList);
}
return null;
}
@Override
public boolean supports(Class<?> authentication)
{
return authentication.equals(AdminUsernamePasswordAuthenticationToken.class);
}
}
CustomAuthentictionProvider - For User
@Component
public class UserCustomAuthenticationProvider implements AuthenticationProvider
{
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
String username = authentication.getName();
String password = authentication.getCredentials().toString();
if (username.equals("user") && password.equals("user@123#"))
{
List<GrantedAuthority> authorityList = new ArrayList<>();
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
authorityList.add(authority);
return new UserUsernamePasswordAuthenticationToken(username, password, authorityList);
}
return null;
}
@Override
public boolean supports(Class<?> authentication)
{
return authentication.equals(UserUsernamePasswordAuthenticationToken.class);
}
}
CustomHandlers required for CustomFilter
@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler
{
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException
{
response.sendRedirect(request.getContextPath() + "/login?error=true");
}
}
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler
{
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException
{
HttpSession session = request.getSession();
if (session != null)
{
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
response.sendRedirect(request.getContextPath() + "/app/user/dashboard");
}
}
And finally SpringSecurityConfiguration
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
DataSource dataSource;
@Autowired
private AdminCustomAuthenticationProvider adminCustomAuthenticationProvider;
@Autowired
private UserCustomAuthenticationProvider userCustomAuthenticationProvider;
@Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
@Autowired
private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(adminCustomAuthenticationProvider);
auth.authenticationProvider(userCustomAuthenticationProvider);
}
@Bean
public MyAuthenticationFilter myAuthenticationFilter() throws Exception
{
MyAuthenticationFilter authenticationFilter = new MyAuthenticationFilter();
authenticationFilter.setAuthenticationSuccessHandler(customAuthenticationSuccessHandler);
authenticationFilter.setAuthenticationFailureHandler(customAuthenticationFailureHandler);
authenticationFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));
authenticationFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationFilter;
}
@Override
protected void configure(final HttpSecurity http) throws Exception
{
http
.addFilterBefore(myAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.csrf().disable()
.authorizeRequests()
.antMatchers("/resources/**", "/", "/login")
.permitAll()
.antMatchers("/config/*", "/app/admin/*")
.hasRole("ADMIN")
.antMatchers("/app/user/*")
.hasAnyRole("ADMIN", "USER")
.antMatchers("/api/**")
.hasRole("APIUSER")
.and().exceptionHandling()
.accessDeniedPage("/403")
.and().logout()
.logoutSuccessHandler(new CustomLogoutSuccessHandler())
.invalidateHttpSession(true);
http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired=true");
}
}
Hope it will help to understand configuring multiple authentication without fallback authentication.