Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with Spring Security remember me token not being set on SecurityContextHolder

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;
    }

}
like image 232
balteo Avatar asked Apr 10 '15 14:04

balteo


1 Answers

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 your UsernamePasswordAuthenticationFilter.setRememberMeServices() property, include the RememberMeAuthenticationProvider in your AuthenticationManager.setProviders() list, and add RememberMeAuthenticationFilter into your FilterChainProxy (typically immediately after your UsernamePasswordAuthenticationFilter)

You need to make the following changes:

  1. In configure() method you need to add a key and filters

    http.rememberMe().key("yourKey")

    .addFilter(usernamePasswordAuthenticationFilter()) .addFilter(rememberMeAuthenticationFilter())

  2. 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;
    }
    
  3. 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"));
    }
    
like image 107
Mithun Avatar answered Oct 01 '22 11:10

Mithun