I am encountering an issue with my remember me configuration:
[nio-8080-exec-8] s.s.w.a.r.RememberMeAuthenticationFilter : SecurityContextHolder not populated with remember-me token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@73939efa: Principal: Member ...
Here is my Spring security configuration:
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private MemberUserDetailsService memberUserDetailsService;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
private AccessDecisionManager accessDecisionManager;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Override
protected void configure(HttpSecurity http) throws Exception {
//@formatter:off
http
.headers()
.cacheControl()
.and()
.and()
.csrf()
.csrfTokenRepository(csrfTokenRepository())
.and()
.rememberMe()
.tokenValiditySeconds(60*60*24*7)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.formLogin()
.loginProcessingUrl("/api/signin")
.failureHandler(authenticationFailureHandler())
.successHandler(authenticationSuccessHandler())
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/api/signout"))
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.authorizeRequests()
.accessDecisionManager(accessDecisionManager)
.antMatchers("/resources/**", "/**").permitAll()
.anyRequest().authenticated();
//@formatter:on
}
private LogoutSuccessHandler logoutSuccessHandler() {
return new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setStatus(HttpStatus.OK.value());
}
};
}
private AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
// TODO: deal with InvalidCsrfTokenException
response.setStatus(HttpStatus.FORBIDDEN.value());
}
};
}
private AuthenticationFailureHandler authenticationFailureHandler() {
return new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
};
}
private AuthenticationSuccessHandler authenticationSuccessHandler() {
return new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Member member = (Member) authentication.getPrincipal();
eventPublisher.publishEvent(new SigninApplicationEvent(member));
// TODO: overhaul below
response.addHeader("MEMBER_ROLE", member.getRole().name());
response.setStatus(HttpStatus.OK.value());
}
};
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberUserDetailsService).passwordEncoder(passwordEncoder);
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
and also:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CoreSecurityConfiguration {
@Bean
public MemberUserDetailsService memberUserDetailsService() {
return new MemberUserDetailsService();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder;
}
@Bean
public SessionRegistryImpl sessionRegistry() {
SessionRegistryImpl sessionRegistry = new SessionRegistryImpl();
return sessionRegistry;
}
@Bean
public AffirmativeBased accessDecisionManager() {
AffirmativeBased accessDecisionManager = new AffirmativeBased(accessDecisionVoters());
return accessDecisionManager;
}
private List<AccessDecisionVoter<? extends Object>> accessDecisionVoters() {
List<AccessDecisionVoter<? extends Object>> accessDecisionVoters = new ArrayList<>();
accessDecisionVoters.add(roleHierarchyVoter());
accessDecisionVoters.add(webExpressionVoter());
return accessDecisionVoters;
}
@Bean
public WebExpressionVoter webExpressionVoter() {
WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
webExpressionVoter.setExpressionHandler(defaultWebSecurityExpressionHandler());
return webExpressionVoter;
}
@Bean
public DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy());
return defaultWebSecurityExpressionHandler;
}
@Bean
public RoleHierarchyVoter roleHierarchyVoter() {
RoleHierarchyVoter roleHierarchyVoter = new RoleHierarchyVoter(roleHierarchy());
return roleHierarchyVoter;
}
@Bean
public RoleHierarchyImpl roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
//@formatter:off
roleHierarchy.setHierarchy(
"ROLE_ADMINISTRATOR > ROLE_MODERATOR\n" +
"ROLE_MODERATOR > ROLE_SUBSCRIBED_PARENTS\n" +
"ROLE_MODERATOR > ROLE_SUBSCRIBED_CHILDCARE_WORKER\n" +
"ROLE_SUBSCRIBED_PARENTS > ROLE_BASIC_PARENTS\n" +
"ROLE_SUBSCRIBED_CHILDCARE_WORKER > ROLE_BASIC_CHILDCARE_WORKER");
//@formatter:on
return roleHierarchy;
}
}
Can somemone please help?
edit 1:
MemberUserDetailsService:
@Component
public class MemberUserDetailsService implements UserDetailsService {
@Autowired
private MemberRepository memberRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Member member = memberRepository.findByEmail(email);
if (member == null) {
throw new UsernameNotFoundException("Username: " + email + " not found!");
}
return member;
}
}
edit 2: Here is the new config:
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private MemberUserDetailsService memberUserDetailsService;
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Autowired
private AccessDecisionManager accessDecisionManager;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private CsrfTokenRepository csrfTokenRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
//@formatter:off
http
.headers()
.cacheControl()
.and()
.and()
.csrf()
.csrfTokenRepository(csrfTokenRepository())
.and()
.rememberMe()
.key("myKey")
.tokenValiditySeconds(60*60*24*7)
.userDetailsService(memberUserDetailsService)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.formLogin()
.loginProcessingUrl("/api/signin")
.failureHandler(authenticationFailureHandler())
.successHandler(authenticationSuccessHandler())
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/api/signout"))
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.addFilter(usernamePasswordAuthenticationFilter())
.addFilter(rememberMeAuthenticationFilter())
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.authorizeRequests()
.accessDecisionManager(accessDecisionManager)
.antMatchers("/resources/**", "/**").permitAll()
.anyRequest().authenticated();
//@formatter:on
}
private LogoutSuccessHandler logoutSuccessHandler() {
return new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setStatus(HttpStatus.OK.value());
}
};
}
private AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
// TODO: deal with InvalidCsrfTokenException & MissingCsrfTokenException
response.setStatus(HttpStatus.FORBIDDEN.value());
}
};
}
private AuthenticationFailureHandler authenticationFailureHandler() {
return new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
};
}
private AuthenticationSuccessHandler authenticationSuccessHandler() {
return new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setStatus(HttpStatus.OK.value());
Member member = (Member) authentication.getPrincipal();
eventPublisher.publishEvent(new SigninApplicationEvent(member));
response.setStatus(HttpStatus.OK.value());
// TODO: overhaul below
response.addHeader("MEMBER_ROLE", member.getRole().name());
}
};
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(rememberMeAuthenticationProvider()).userDetailsService(memberUserDetailsService).passwordEncoder(passwordEncoder);
}
@Bean
protected CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
@Bean
public RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
return new RememberMeAuthenticationProvider("myKey");
}
@Bean
public RememberMeServices rememberMeServices() {
return new TokenBasedRememberMeServices("myKey", memberUserDetailsService);
}
@Bean
public RememberMeAuthenticationFilter rememberMeAuthenticationFilter() throws Exception {
return new RememberMeAuthenticationFilter(authenticationManager(), rememberMeServices());
}
@Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();
filter.setRememberMeServices(rememberMeServices());
filter.setAuthenticationManager(authenticationManager());
return filter;
}
}
Since you have not specified the remember-me service implementation type, TokenBasedRememberMeServices
is used by default.
Please find the below note from the documentation when using TokenBasedRememberMeServices
:
Don't forget to add your
RememberMeServices
implementation to yourUsernamePasswordAuthenticationFilter.setRememberMeServices()
property, include theRememberMeAuthenticationProvider
in yourAuthenticationManager.setProviders()
list, and add RememberMeAuthenticationFilter into yourFilterChainProxy
(typically immediately after yourUsernamePasswordAuthenticationFilter
)
You need to make the following changes:
In configure()
method you need to add a key and filters
http.rememberMe().key("yourKey")
.addFilter(usernamePasswordAuthenticationFilter())
.addFilter(rememberMeAuthenticationFilter())
Create UsernamePasswordAuthenticationFilter
and RememberMeAuthenticationFilter
@Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter()
throws Exception {
UsernamePasswordAuthenticationFilter filter =
new UsernamePasswordAuthenticationFilter();
filter.setRememberMeServices(memberUserDetailsService);
filter.setAuthenticationManager(authenticationManager());
return filter;
}
@Bean
public RememberMeAuthenticationFilter rememberMeAuthenticationFilter()
throws Exception {
RememberMeAuthenticationFilter filter =
new RememberMeAuthenticationFilter(authenticationManager(), memberUserDetailsService);
return filter;
}
Add RememberMeAuthenticationProvider
to the list of providers:
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(memberUserDetailsService)
.passwordEncoder(passwordEncoder)
.and()
.authenticationProvider(new RememberMeAuthenticationProvider("yourKey"));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With