在SpringSecurity中从另一个控制器手动调用failurehandler?

6ioyuze2  于 2021-07-24  发布在  Java
关注(0)|答案(1)|浏览(362)

inb4:一般来说,我对spring非常陌生,所以我知道处理springsecurity是一个艰难的过程。
热释光;dr:我可以从我的自定义successhandler向failurehandler“抛出异常”吗?
目前,我正在尝试实现一些行为与我们的登录页面。如果凭据错误,用户将被重定向回登录,并显示一条错误消息。如果所使用的凭据是正确的,但另一个人已经在使用该帐户,则用户将被重定向到登录,并显示一条消息错误:“max sessions error”。
到目前为止,将exceptionmappingauthenticationfailurehandler与springs内置的会话/并发控制一起使用可以正常工作,显示正确的错误消息,但是会话控制并没有像我预期的那样工作。这可能是我自己的配置不好,或者只是因为我不太理解文档(英语不是我的母语,所以我完全接受这种可能性)。我希望sessioncontrol的行为是,如果与给定用户的会话处于活动状态,则尝试登录的人将被踢回登录。目前它是这样做的,但是如果会话处于活动状态的用户注销并尝试重新登录,它将提示他/她以maxsessionsrerror登录。因为sessionmanagement.maximumsessions()限制了一个帐户的登录次数,而不是用户可以同时拥有的活动会话数。我可能错了,但我还在调查。我已经试着按照文档所说的设置我的配置,但是它没有像我期望的那样工作。
我目前的替代方法是,当我确认其他人当前正在使用帐户时,尝试在successhandler上引发异常,以便failurehandler可以将用户重定向到带有正确错误消息的登录页。我的理解是,如果我重定向到登录页面,会话不会被破坏。我可以试着自己毁掉它,但我诚实地相信spring会这么做,因为我缺乏经验,所以我提出了这个问题。
欢迎任何帮助或掌掴。我想学习,到目前为止我对安全有点疏忽。下面是config和successhandler的代码。小提示:这个项目没有使用web.xml,因为我不知道什么原因,所以我试着将大多数配置转换为web.xml @Bean 符号
配置

/*Other beans and configs*/

@Bean
    ExceptionMappingAuthenticationFailureHandler exceptionMappingAuthenticationFailureHandler(){
        ExceptionMappingAuthenticationFailureHandler ex = new ExceptionMappingAuthenticationFailureHandler();
        String RUTAERROR1 = "/login.zul?login_error=1";
        String RUTAERROR2 = "/login.zul?login_error=2";
        Map<String, String> errores = new HashMap<>();
        errores.put(org.springframework.security.authentication.ProviderNotFoundException.class.getCanonicalName(), RUTAERROR1);
        errores.put(SessionAuthenticationException.class.getCanonicalName(), RUTAERROR2);
        ex.setExceptionMappings(errores);
        return ex;
    }

@Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
        .antMatchers("/zkau/**").permitAll()
        .antMatchers("/css/**").permitAll()
        .antMatchers("/services/**").permitAll()
        .antMatchers("/images/**").permitAll()
        .antMatchers("/forgotpassword.zul").permitAll()
        .antMatchers("/login.zul**").permitAll()
        .antMatchers("/**").authenticated()
        .and()
        .formLogin()
        .loginProcessingUrl("/j_spring_security_check")
        .successHandler(customSuccessHandler())
        .loginPage("/login.zul")
        .permitAll()
//      .defaultSuccessUrl("/index.zul")
//      .defaultSuccessUrl("/menuAgrupadores.zul").
        .failureHandler(exceptionMappingAuthenticationFailureHandler())
//      .failureUrl("/login.zul?login_error=1")
        .and()
        .logout()
//      .logoutSuccessUrl("/login.zul")
        .logoutUrl("/j_spring_security_logout")
        .deleteCookies("JSESSIONID")
        .invalidateHttpSession(true)
        .logoutSuccessHandler(logoutSuccessHandler())
        .and().csrf().disable();

        http.headers()
        .frameOptions().sameOrigin()
        .httpStrictTransportSecurity().disable();

        http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
    }

成功处理程序

public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Resource
    private UsuariosRepository usuariosRepository;
    private RedirectStrategy redirect = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth)
            throws IOException, ServletException {
        Usuarios user = usuariosRepository.findByUsuario(auth.getName());
        if(user.getToken() != null) {
            throw new SessionLimitException("Max sessions limit reached!", new ProviderNotFoundException("Máximo de sesiones excedido.")); //This is what I'm trying to do
        } else {
            user.setToken(request.getSession().getId());
            usuariosRepository.save(user);
            redirect.sendRedirect(request, response, "/menuAgrupadores.zul");
        }

    }

sessionlimitexception只是扩展了authenticationexception,没有进一步的逻辑。
编辑
正如@eleftheria stein kousathana所说,我缺少了侦听器,但我必须将其添加到web.xml中。就文档而言,这是很明显的,但是我的应用程序没有这个文件,其他所有bean/config都正常运行。换句话说,httpsessioneventpublisher的@bean符号在我的示例中不起作用。如果您遇到同样的问题,我强烈建议您尝试@bean和xml两种方法来注册这个侦听器。如果您不知道,web.xml应该放在web-inf中。
工作代码:

@Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/zkau/**").permitAll()
            .antMatchers("/css/**").permitAll()
            .antMatchers("/services/**").permitAll()
            .antMatchers("/images/**").permitAll()
            .antMatchers("/forgotpassword.zul").permitAll()
            .antMatchers("/login.zul**").permitAll()
            .antMatchers("/**").authenticated()
        .and()
            .formLogin()
            .loginProcessingUrl("/j_spring_security_check")
            .loginPage("/login.zul")
            .permitAll()
            .defaultSuccessUrl("/menuAgrupadores.zul")
            .failureHandler(exceptionMappingAuthenticationFailureHandler())
        .and()
            .logout()
            .logoutUrl("/j_spring_security_logout")
            .deleteCookies("JSESSIONID")
            .invalidateHttpSession(true)
            .logoutSuccessUrl("/login.zul")
        .and()
            .csrf()
            .disable();

        http.headers()
            .frameOptions()
            .sameOrigin()
            .httpStrictTransportSecurity()
            .disable();

        http.sessionManagement()
            .maximumSessions(1)
            .maxSessionsPreventsLogin(true)
            .sessionRegistry(sessionRegistry()); //Added this for good measure

httpsessioneventpublisher的xml表示法

<listener>
        <listener-class>
            org.springframework.security.web.session.HttpSessionEventPublisher
        </listener-class>
    </listener>
i7uaboj4

i7uaboj41#

我建议坚持第一种方法。
你唯一缺少的就是一个 HttpSessionEventPublisher .

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
    return new HttpSessionEventPublisher();
}

一旦你添加了这个,它将解决注销问题,一个新的用户将被允许登录。
如果没有这个 HttpSessionDestroyedEvent 未在注销时发布到应用程序上下文,并且未清理过期的会话。
这就是为什么不允许新用户登录的原因。
通知 HttpSecurity.sessionManagement javadoc声明:
使用sessionmanagementconfigurer.maximumsessions(int)时,不要忘记为应用程序配置httpsessioneventpublisher,以确保清除过期的会话。

相关问题