In a spring mvc application i am capturing an additional 'location' parameter on login screen and using it for authentication in addition to username. So in 'loadUserByUsername' my sql query is something like,
select from user where username = ? and location = ?
Now, if user is a remember-me user then there is no way to capture 'location' parameter because there won't be a login prompt. In case of remember-me feature spring stores only username in cookie. And for remember-me login it then retrieves that username from cookie and passes it to 'loadUserByUsername' call to load user from DB. So, in my case, for remember-me users, since 'location' is null query to load user fails.
I wanted to know if there is a way to override the default spring behavior and store 'location' in cookie along with username and then pass location and username to 'loadUserByUsername' in PersistentTokenBasedRememberMeServices.processAutoLoginCookie().
Please see my code below for reference,
CustomAuthenticationFilter.java :-
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
final Long locationId = Long.parseLong(request.getParameter("locations"));
request.getSession().setAttribute("LOCATION_ID", locationId);
return super.attemptAuthentication(request, response);
}
}
SecurityConfig.java:-
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DataSource dataSource;
@Autowired
private AuthenticationManagerBuilder auth;
@Autowired
public void configureGlobal(UserDetailsService userDetailsService, AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
AccessDeniedExceptionHandler accessDeniedExceptionHandler;
@Bean
public CustomInvalidSessionStrategy invalidSessionStrategy() {
return new CustomInvalidSessionStrategy();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/error/**").permitAll()
.antMatchers("/secured/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
// .defaultSuccessUrl("/")
.permitAll()
.and().rememberMe().rememberMeServices(persistentTokenBasedRememberMeServices())
.and()
.logout()
.permitAll()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedExceptionHandler);
http.addFilterBefore(customAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(rememberMeAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() {
AuthenticationManager manager = null;
try {
manager = super.authenticationManagerBean();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return manager;
}
@Bean
public SimpleUrlAuthenticationSuccessHandler simpleUrlAuthenticationSuccessHandler() {
SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
handler.setDefaultTargetUrl("/");
return handler;
}
@Bean
public SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() {
SimpleUrlAuthenticationFailureHandler handler = new SimpleUrlAuthenticationFailureHandler();
handler.setDefaultFailureUrl("/login?error");
return handler;
}
@Bean
public CustomAuthenticationFilter customAuthenticationFilter () {
CustomAuthenticationFilter filter= new CustomAuthenticationFilter();
filter.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/login","POST"));
filter.setAuthenticationManager(authenticationManagerBean());
filter.setUsernameParameter("username");
filter.setPasswordParameter("password");
filter.setAuthenticationSuccessHandler(simpleUrlAuthenticationSuccessHandler());
filter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler());
filter.setRememberMeServices(persistentTokenBasedRememberMeServices());
return filter;
}
@Bean
public RememberMeAuthenticationFilter rememberMeAuthenticationFilter() {
RememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setRememberMeServices(persistentTokenBasedRememberMeServices());
return filter;
}
@Bean
public PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices() {
PersistentTokenBasedRememberMeServices service = new PersistentTokenBasedRememberMeServices("remember_me_key", userDetailsService, persistentTokenRepository());
service.setCookieName("remember_me");
service.setTokenValiditySeconds(864000);
return service;
}
@Autowired
public UserDetailsService userDetailsService;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
tokenRepositoryImpl.setDataSource(dataSource);
return tokenRepositoryImpl;
}
}
If you want to override the default spring behavior and store extra parameters in cookies you should implement the Spring's interface
UserDetails
. There is an example how to do itTo get logged user you can use
And for using
loadUserByUsername
you should to implement interfaceUserDetailsService
. For exampleExtend class PersistentTokenBasedRememberMeServices and override its method as follow