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

时间:2019-10-11 09:27:04

标签: java spring spring-mvc spring-security logout

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

  • /user
  • /admin

以下按预期工作:

  • 访问受保护资源时重定向到登录页面
  • 凭据为假时使用?error参数重定向到登录页面
  • 根据定义的角色防止对受保护资源的访问

以下内容无效:

  • 注销后重定向到登录页面

注销时,我重定向到/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中触发它。

我也尝试过:

  • 将登录和注销网址移出路径/user->破坏登录的路径
  • 使用Order(1)定义第三种配置,明确允许登录和注销页面上的GET和POST->也破坏了登录
  • 不使用.antMatcher而是使用.antMatchers,但是我想我将不能拥有两个不同的FormLogins

1 个答案:

答案 0 :(得分:1)

我知道这类问题会引起很大的问号,并会通过思考为什么

解决方案将会是。

@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")。将不会收到在查询字符串中传递的任何参数。

因此,解决方案将始终使登录页面成为不受限制的资源。