Spring Security的问题记得我没有在SecurityContextHolder上设置令牌

时间:2015-04-10 14:17:05

标签: spring spring-security remember-me

我遇到了一个问题,我记得我的配置:

[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 ...

这是我的Spring安全配置:

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

还有:

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

}

请问有人可以帮忙吗?

编辑1

MemberUserDetailsS​​ervice:

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

}

编辑2 :这是新配置:

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

}

1 个答案:

答案 0 :(得分:3)

由于您尚未指定remember-me服务实现类型,因此默认使用TokenBasedRememberMeServices

使用TokenBasedRememberMeServices时,请从文档中找到以下注释:

  

不要忘记将RememberMeServices实施添加到您的   UsernamePasswordAuthenticationFilter.setRememberMeServices()财产,   在你的RememberMeAuthenticationProvider中添加AuthenticationManager.setProviders()   FilterChainProxy列表,然后添加   将MeMeuthenticationFilter记住到UsernamePasswordAuthenticationFilter(通常是   在configure())之后立即

您需要进行以下更改:

  1. http.rememberMe().key("yourKey")方法中,您需要添加密钥和过滤器

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

    UsernamePasswordAuthenticationFilter

  2. 创建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; }

    RememberMeAuthenticationProvider
  3. @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(memberUserDetailsService) .passwordEncoder(passwordEncoder) .and() .authenticationProvider(new RememberMeAuthenticationProvider("yourKey")); } 添加到提供商列表中:

    {{1}}