从 CustomLogoutHandler 到 'login?logout' 的重定向被重定向到 'login'

Redirect from CustomLogoutHandler to 'login?logout' is redirected to 'login'

我有两个安全区域,我为其提供基于表单的登录。

以下按预期工作:

以下无效:

注销时,我重定向到 /user/login?logout 以显示一条消息“您已注销”,表明注销成功。但是,这不起作用,而是将重定向到 /user/login?logout 重定向到 /user/login,因此不会显示任何消息。

当我删除自定义注销处理程序 .logoutSuccessHandler(logoutSuccessHandler()) 并改为包含 .logoutSuccessUrl("/user/login?logout").permitAll() 时,它起作用了!

但是我希望此处理程序在注销时执行其他操作。

@Configuration
@Order(1)
public static class FormLoginUser extends WebSecurityConfigurerAdapter {

    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return new UserCustomAccessDeniedHandler();
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        return new UserCustomLogoutSuccessHandler();
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
                return new UserCustomAuthenticationSuccessHandler();
    }
    private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource() {
        return WebAuthenticationDetails::new;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.antMatcher("/user/**")
                .authorizeRequests().anyRequest().hasRole("USER")
                .and()
                .formLogin()
                .loginPage("/user/login")
                .permitAll()
                .authenticationDetailsSource(authenticationDetailsSource())
                .successHandler(authenticationSuccessHandler())
                .and()
                .logout()
                .logoutUrl("/user/logout")
                .logoutSuccessUrl("/user/login?logout").permitAll()
                .logoutSuccessHandler(logoutSuccessHandler())
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler())
        ;

    }
}

@Configuration
@Order(2)
public static class FormLoginAdmin extends WebSecurityConfigurerAdapter {

    @Bean
    public AccessDeniedHandler adminAccessDeniedHandler() {
        return new AdminCustomAccessDeniedHandler();
    }

    @Bean
    public LogoutSuccessHandler adminLogoutSuccessHandler() {
        return new AdminCustomLogoutSuccessHandler();
    }

    @Bean
    public AuthenticationSuccessHandler adminAuthenticationSuccessHandler() {
        return new AdminCustomAuthenticationSuccessHandler();
    }
    private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource() {
        return WebAuthenticationDetails::new;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.antMatcher("/admin/**")
                .authorizeRequests().anyRequest().hasRole("ADMIN")
                .and()
                .formLogin()
                .loginPage("/admin/login")
                .authenticationDetailsSource(authenticationDetailsSource())
                .successHandler(adminAuthenticationSuccessHandler())
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/admin/logout")
                .logoutSuccessHandler(adminLogoutSuccessHandler())
                .and()
                .exceptionHandling().accessDeniedHandler(adminAccessDeniedHandler());

    }
}

这是我的 CustomLogoutHandler:

public class UserCustomLogoutSuccessHandler extends
        SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
    public void onLogoutSuccess(
            HttpServletRequest request,
            HttpServletResponse response,
            Authentication authentication)
            throws IOException, ServletException {
        this.setDefaultTargetUrl("/user/login?logout");
        super.onLogoutSuccess(request, response, authentication);
    }
}

在 DevTools 中,我可以清楚地看到,每当我尝试访问 URL /user/login?logout 时,GET-Request 结果为 302,然后为 user/login 发出新请求.当我在浏览器 URL 行中手动编辑 URL 或从应用程序通过 FORM-Post 触发注销时,就会发生这种情况。

当我删除 logoutSuccessHandler 时,我可以在浏览器中手动输入 URL 并在应用程序中从我的 FORM-Post 触发它。

我也试过:

我知道这类问题会打一个大大的问号,让人挠头想WHY?

解决方案是。

@Override
protected void configure(HttpSecurity http) throws Exception 
{
    http
        .antMatcher("/user/**").authorizeRequests()
        .antMatchers("/user/login").permitAll() //solution
        .anyRequest().hasRole("USER")
        .and()
        .formLogin()
        .loginPage("/user/login")
        .permitAll()
        .authenticationDetailsSource(authenticationDetailsSource())
        .successHandler(authenticationSuccessHandler())
        .and()
        .logout()
        .logoutUrl("/user/logout")
        .logoutSuccessUrl("/user/login?logout").permitAll()
        .logoutSuccessHandler(logoutSuccessHandler())
        .and()
        .exceptionHandling().accessDeniedHandler(accessDeniedHandler());
}

想知道为什么吗?

从以前的配置

您的资源 /user/login 是受限资源,仅当
时才允许使用 authenticated = truehasRole = "User"
在您的注销成功处理程序中,您使会话无效并重定向到 /user/login?logout 页面,但 /user/login 是受限资源,因此 FilterSecurityInterceptor 将重定向到配置的登录页面(.loginPage("/user/login")。因此你将不会收到任何传入查询字符串的参数。

因此解决方案总是将登录页面设为不受限制的资源。