我在记住我的配置时遇到问题:

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

}

最佳答案

由于尚未指定“记住我”服务实现类型,因此默认情况下使用TokenBasedRememberMeServices

使用TokenBasedRememberMeServices时,请从文档中找到以下说明:



您需要进行以下更改:

  • configure()方法中,您需要添加密钥和过滤器
    http.rememberMe().key("yourKey").addFilter(usernamePasswordAuthenticationFilter()).addFilter(rememberMeAuthenticationFilter())
  • 创建UsernamePasswordAuthenticationFilterRememberMeAuthenticationFilter
    @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添加到提供者列表:
    @Override
    protected void configure(AuthenticationManagerBuilder auth)
        throws Exception {
        auth.userDetailsService(memberUserDetailsService)
            .passwordEncoder(passwordEncoder)
            .and()
            .authenticationProvider(new RememberMeAuthenticationProvider("yourKey"));
    }
    
  • 08-03 19:48