如何注销时使我记住?

时间:2019-01-09 08:35:45

标签: java spring-mvc spring-security

好吧,我没有实现PersistentTokenBasedRememberMeServices,所以我不能使用.logout(request, response, auth)。但是我使用JdbcTokenRepositoryImpl以便将PersistentTokenRepository用于“记住我”功能。

LogoutController:

@Controller
public class LogoutController {

    @RequestMapping(value = {"/logout"}, method = RequestMethod.GET)
    public String logout() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if(auth != null) {
            SecurityContextHolder.getContext().setAuthentication(null);
        }

        return "redirect:/login?logout";
    }
}

安全配置:

@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authProvider());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/", "/playground").hasAnyRole("ROOT", "MODER", "USER")
            .antMatchers("/users/**").hasAnyRole("ROOT", "MODER")
        .and()
            .formLogin().loginPage("/login").loginProcessingUrl("/login").failureHandler(customAuthenticationFailureHandler())
        .and()
            .rememberMe().rememberMeParameter("remember-me").tokenRepository(persistentTokenRepository()).userDetailsService(userDetailsService)
        .and()
            .logout().logoutUrl("/logout");
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider authProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setPasswordEncoder(passwordEncoder());
        authProvider.setUserDetailsService(userDetailsService);
        return authProvider;
    }

    @Bean
    public AuthenticationFailureHandler customAuthenticationFailureHandler() {
        return new CustomAuthenticationFailureHandler();
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }

当我使用“记住我”身份登录时,便无法退出。我猜是因为记住我的功能。我应该添加什么到LogoutController才能进行正确的注销过程?

注意:问题是,如果我仅在注销时使用POST方法,那么它就可以正常工作,但是我想使用GET方法,因此我必须创建一个注销控制器来执行get方法。

2 个答案:

答案 0 :(得分:2)

尝试禁用crsf(http.csrf().disable())。

spring的安全性注销过滤器中的默认实现为:

        if (http.getConfigurer(CsrfConfigurer.class) != null) {
            this.logoutRequestMatcher = new AntPathRequestMatcher(this.logoutUrl, "POST");
        }
        else {
            this.logoutRequestMatcher = new OrRequestMatcher(
                new AntPathRequestMatcher(this.logoutUrl, "GET"),
                new AntPathRequestMatcher(this.logoutUrl, "POST"),
                new AntPathRequestMatcher(this.logoutUrl, "PUT"),
                new AntPathRequestMatcher(this.logoutUrl, "DELETE")
            );
        }

如您所见,您的Csrf是否已启用(即使您改写了protected void configure(HttpSecurity http),默认情况下也已启用),那么只有POST方法有效,如果不是全部的话。

顺便说一句:您确定您的请求到达了LogoutController,因为我认为它使用的是标准的Spring Security注销机制? (要禁用它,请http.logout().disable(),与默认情况下启用的csrf相同)

答案 1 :(得分:1)

总结一下。

我设法测试了几种方法,下面是我所得到的:

  1. 正如Deinum M.在评论中所建议的那样,有可能不使用控制器,但仍然可以通过GET请求注销。 Here it is.

.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))

  1. 正如Andrew Sasha在答案部分中所建议的那样,您可以禁用csrf,因为它有意阻止使用GET请求。现在,您可以使用GET请求注销,甚至无需使用任何控制器即可。

http.csrf().disable()

  1. 如果您仍然希望使用控制器,则将为您提供帮助

.deleteCookies("remember-me", "JSESSIONID")
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/")

(我不确定,但是我觉得它不起作用,因为您执行GET请求并使用控制器来控制注销)

  1. 所以您可以通过编程方式完成

首先,在Spring安全配置中为“记住我的cookie”添加一个名称:

rememberMe().rememberMeCookieName("remember-me")

然后在注销控制器中添加以下内容:

String cookieName = "remember-me";
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
response.addCookie(cookie);

这里唯一的问题是您必须手动从persistent_logins表中删除一条记录

(为了获得请求和响应,您只需将它们传递给方法public void logout(HttpServletRequest request, HttpServletResponse response)

  1. 可以使用POST请求,但可以在JavaScript甚至纯HTML和CSS的帮助下将其用作链接。

您可以在this topic上找到解决方案。

那我们在这里有什么?

总结上面的所有内容,我可以说,如果您想要一个控制器,则必须自己以编程方式编写所有内容(有人会说这是车轮的重塑)。

仍然可以使用GET请求,但不使用列表的第一和第二位置描述的控制器。

(使用GET请求的后果写在CSRF Documentation中,由于它的无漏洞性,不建议使用GET请求。)

因此,我决定成为我的最爱的最后一件事是借助JS或HTML和CSS使POST请求看起来像GET请求(将其用作链接)。而且,当您使用POST请求时,您就会获得CSRF保护。

我希望这会对某人有所帮助。