从Spring Security中的另一个控制器手动调用failureHandler吗?

杰托·马丁内斯(Jetto Martinez)

inb4:一般来说,我对Spring还是很陌生的,所以我知道与SpringSecurity打交道非常困难。

tl; dr:我可以从自定义的SuccessHandler到FailureHandler“抛出异常”吗?

目前,我正在尝试通过我们的登录页面实现某些行为。万一凭据错误,用户将被重定向回登录,并显示一条消息错误。如果使用的凭据正确,但是其他人已经在使用该帐户,则用户将被重定向到“登录”,并显示一条错误消息(“最大会话错误”)。

到目前为止,使用带有内置于会话/并发控件中的Springs的ExceptionMappingAuthenticationFailureHandler可以正常工作,显示正确的错误消息,但是会话控件无法正常工作。这可能是我个人的配置错误,或者仅仅是我对文档没有真正的了解(英语不是我的母语,所以我完全可以接受)。我希望从SessionControl中获得的行为是,如果与给定用户的会话处于活动状态,则试图加入该人员的人将被踢回到登录名。当前,它可以执行此操作,但是如果会话活动的用户注销并尝试重新登录,它将踢他/她使用MaxSessionsError登录。这是预期的AFAIU,因为SessionManagement.MaximumSessions()限制了帐户的登录数量,而不是用户可以同时拥有的活动会话数。我可能是错的,但我仍在调查中。我尝试设置我的配置如文档所述,但是它没有按我期望的那样工作。

我当前的替代方法是在确认其他人当前正在使用帐户时尝试在SuccessHandler上引发Exception,以便FailureHandler可以使用正确的错误消息将用户重定向到Login页面。我的理解是,如果我重定向到“登录”页面,则会话不会被破坏。我可以尝试自己销毁它,但老实说,由于我缺乏经验,因此我更相信Spring可以做到这一点,因此我提出了问题。

欢迎任何帮助或一巴掌。我想学习,到目前为止我对安全性有点疏忽。下面是config和successHandler的代码。小提示:由于某些我不十分了解的原因,该项目未使用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);
    }

SuccessHandler

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>
Eleftheria Stein-Kousathana

我建议坚持第一种方法。

您唯一缺少的是一个HttpSessionEventPublisher

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

添加此选项后,它将解决注销问题,并允许新用户登录。

没有这个,HttpSessionDestroyedEvent注销时就不会将其发布到应用程序上下文中,也不会清除过期的会话。
这就是为什么不允许新用户登录。

请注意,在HttpSecurity.sessionManagementJavadoc中声明:

使用SessionManagementConfigurer.maximumSessions(int)时,请不要忘记为应用程序配置HttpSessionEventPublisher,以确保清除过期的会话。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Spring MVC,从控制器内部调用另一个控制器

在扩展另一个类的控制器的方法上使用Spring Security批注

如何在Spring MVC中将一个控制器调用到另一个控制器URL?

Spring @Around Aspect在一个控制器中被忽略,但在另一个控制器中未被忽略

将错误消息下载或重定向到Spring Web MVC中的另一个控制器操作

Spring Boot将值从控制器更改为另一个

使用 Spring Security 在应用程序中手动创建第一个用户

Spring控制器:我可以在每个@RequestMapping方法被调用之前调用一个方法吗?

如何从我的Spring Boot应用程序控制器创建对另一个API的PATCH请求?

在Spring-Boot中从服务器调用另一个Rest API

在Laravel中从另一个控制器调用控制器是一种好习惯吗?

手动调用Spring注释验证

如何将rest控制器添加到spring引导库,并在另一个应用程序中使用它?

将json作为正文传递,并将另一个对象作为参数传递给spring控制器

从另一个调用Spring服务类

在权限中获取另一个权限-Spring Security

在JSP中链接另一个页面并保留当前的Spring Security Session

一个AngularJS控制器可以调用另一个吗?

如何从控制器动作中调用另一个控制器动作?

如何从Mvc中的控制器调用另一个控制器操作

存储值并在同一个控制器 Spring MVC 中再次使用

在Spring中管理由一个控制器的不同实例创建的线程

多个控制器适合于Spring框架中的一个实体

从另一个控制器调用控制器是一个坏主意吗?

Spring AOP不适用于另一个方法中的方法调用

Spring Boot从请求中获取承载令牌并调用另一个微服务

在另一个控制器Ember中调用一个controllers方法

如何在另一个控制器的方法中调用一个控制器的方法

从angularjs中的另一个模块的控制器调用一个模块的控制器功能