Java 从CustomLogoutHandler重定向到';登录-注销';被重定向到';登录';

Java 从CustomLogoutHandler重定向到';登录-注销';被重定向到';登录';,java,spring,spring-mvc,spring-security,logout,Java,Spring,Spring Mvc,Spring Security,Logout,我有两个安全区域,我提供基于表单的登录 /user /admin 以下工作如预期: 访问受保护资源时重定向到登录页面 凭据为false时,重定向到登录页面时出现?错误参数 防止基于定义的角色访问受保护的资源 以下操作不起作用: 注销后重定向到登录页面 当注销时,我重定向到/user/login?logout,以显示一条消息“You'e logged out”,指示注销成功。但是,这不起作用,而是重定向到/user/login?注销被重定向到/user/login,因此不显示任何消息

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

  • /user
  • /admin
以下工作如预期:

  • 访问受保护资源时重定向到登录页面
  • 凭据为false时,重定向到登录页面时出现
    ?错误
    参数
  • 防止基于定义的角色访问受保护的资源
以下操作不起作用:

  • 注销后重定向到登录页面
当注销时,我重定向到
/user/login?logout
,以显示一条消息“You'e logged out”,指示注销成功。但是,这不起作用,而是重定向到
/user/login?注销
被重定向到
/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());

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

当我删除
logoutSuccessHandler
时,我可以在浏览器中手动输入URL,并在应用程序中从我的表单帖子中触发它

我还尝试:

  • 将用于登录和注销的URL移出中断登录的路径
    /user
    ->
  • 使用
    Order(1)
    定义第三个配置,明确允许登录和注销页面上的GET和POST->这也破坏了登录
  • 不要使用
    .antMatcher
    ,而是使用
    .antMatchers
    ,不过我想我将无法拥有两个不同的formlogin
我知道这类问题会给人留下一个很大的问号,让人想为什么

解决方案是。

@覆盖
受保护的无效配置(HttpSecurity http)引发异常
{
http
.antMatcher(“/user/**”).authorizedRequests()
.antMatchers(“/user/login”).permitAll()//解决方案
.anyRequest().hasRole(“用户”)
.及()
.formLogin()
.loginPage(“/user/login”)
.permitAll()
.authenticationDetailsSource(authenticationDetailsSource())
.successHandler(authenticationSuccessHandler())
.及()
.logout()
.logoutUrl(“/user/logout”)
.logoutSuccessUrl(“/user/login?logout”).permitAll()
.logoutSuccessHandler(logoutSuccessHandler())
.及()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
}
想知道原因吗?

以前的配置 您的资源
/user/login
是受限资源,只有在
authenticated=true
hasRole=“User”

在注销成功处理程序中,您正在使会话无效并重定向到
/user/login?注销
页面,但
/user/login
是受限资源,因此
过滤器安全Interceptor
将重定向到配置的登录页面(
.loginPage(“/user/login”)
。因此,您将不会收到传入查询字符串的任何参数

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

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