Spring security - expiredUrl не работает

Мне нужно настроить expired-url в моем приложении Spring MVC. Вот мои усилия, но не имеет никакого эффекта:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .addFilterBefore(adminAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        .addFilterBefore(customerAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        .csrf()
            .disable()
        .authorizeRequests()
            .antMatchers("...", "...", "...").permitAll()
            .anyRequest().authenticated()
        .and()
            .formLogin()
                .loginPage("/admin/login")
        .and()
            .logout()
                .addLogoutHandler(customLogoutHandler())
                .logoutSuccessHandler(customLogoutSuccessHandler())
                .logoutUrl("/logout")
        .deleteCookies("remove")
        .invalidateHttpSession(true)
            .permitAll()
        .and()
        .sessionManagement()
            .maximumSessions(1)
            .expiredUrl("/expired");

}

Это не имеет никакого эффекта, и когда сеанс пользователя заканчивается, Spring не перенаправляет его на URL /expired и просто перенаправляет его на /admin/login url.

Update:

Я пробовал предлагаемые решения в комментариях и ответах, но не видел никакого эффекта. Также я удалил addLogoutHandler(), logoutSuccessHandler() и два addFilterBefore() в начале метода, но не работал.

Также я попробовал другое решение таким образом:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .addFilterBefore(sessionManagementFilter(), SessionManagementFilter.class)
        .csrf()
            .disable()
        .authorizeRequests()
            .antMatchers("...", "...", "...").permitAll()
            .anyRequest().authenticated()
        .and()
            .formLogin()
                .loginPage("/admin/login")
        .and()
            .logout()
                .logoutUrl("/logout")
        .deleteCookies("remove")
        .invalidateHttpSession(true)
            .permitAll();
}

@Bean
public SessionManagementFilter sessionManagementFilter() {
    SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(httpSessionSecurityContextRepository());
    sessionManagementFilter.setInvalidSessionStrategy(simpleRedirectInvalidSessionStrategy());
    return sessionManagementFilter;
}

@Bean
public SimpleRedirectInvalidSessionStrategy simpleRedirectInvalidSessionStrategy() {
    SimpleRedirectInvalidSessionStrategy simpleRedirectInvalidSessionStrategy = new SimpleRedirectInvalidSessionStrategy("/expired");
    return simpleRedirectInvalidSessionStrategy;
}

@Bean
public HttpSessionSecurityContextRepository httpSessionSecurityContextRepository(){
    HttpSessionSecurityContextRepository httpSessionSecurityContextRepository = new HttpSessionSecurityContextRepository();
    return httpSessionSecurityContextRepository;
}

Может ли кто-нибудь помочь мне решить эту проблему?

Ответ 1

Я попробовал решение Али Дехгани (в комментариях) следующим образом:

.sessionManagement().maximumSessions(1).and().invalidSessionUrl("/expired");

И как сказал Coder, добавьте "/expired" в разрешенные URL-адреса и проблема будет решена. Спасибо всем, кто обратил внимание на мою проблему, особенно Али Дехгани и Кодер, за полезные комментарии.

Ответ 2

ConcurrentSessionFilter будет перенаправлен на expiredUrl, если valid идентификатор сеанса помечен как истек в SessionRegistry, см. Spring Ссылка на безопасность:

- expired-url URL-адрес пользователя будет перенаправлен, если они попытаются использовать сеанс, который был "истек" контроллером параллельного сеанса, поскольку пользователь превысил количество разрешенных сеансов и снова вошел в систему в другом месте. Должен быть установлен, если не установлен exception-if-maximum-exceeded. Если значение не указано, сообщение об истечении срока будет просто записано непосредственно обратно в ответ.

SessionManagementFilter перенаправляется на invalidSessionUrl, если идентификатор сеанса не valid (тайм-аут или неправильный идентификатор), см. Spring Ссылка на безопасность:

Если пользователь в настоящее время не аутентифицирован, фильтр проверяет, был ли запрошен неверный идентификатор сеанса (из-за тайм-аута, например), и вызывается настроенный InvalidSessionStrategy, если он установлен. Наиболее распространенное поведение - это просто перенаправить на фиксированный URL-адрес, и он инкапсулирован в стандартную реализацию SimpleRedirectInvalidSessionStrategy. Последнее также используется при настройке неверного URL-адреса сеанса через пространство имен, как описано ранее.

Оба URL (expiredUrl, invalidSessionUrl) должны быть настроены как permitAll().

BTW: Если вы хотите использовать Параллельный контроль сеанса с помощью maximumSessions, вам нужно добавить HttpSessionEventPublisher на ваш web.xml:

Параллельный контроль сеанса

Если вы хотите установить ограничения на возможность доступа к одному пользователю для одного пользователя, Spring Security поддерживает это из коробки со следующими простыми дополнениями. Сначала вам нужно добавить следующего прослушивателя в ваш файл web.xml, чтобы сохранить Spring Security обновленную информацию о событиях жизненного цикла сеанса:

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

Ответ 3

В идеале ваш UX должен просто перенаправить пользователя на страницу входа. Я предполагаю, что вы видите требование наличия выделенной/истекшей страницы из-за Spring MVC - изменение параметров безопасности динамически, где вы сообщили о необходимости иметь отдельные маски входа. Если обходной путь (тот, который я описал в ответе на ваш другой вопрос) работает для вас, вы можете отказаться от своего требования иметь страницу с выделенным/истекшим сроком действия и перенаправить пользователя на правильную страницу входа напрямую, используя номер подхода к решению ( 2). Как насчет этого?

Тем не менее, чтобы ответить на ваш текущий вопрос... Я не уверен, что он работает, но попробуйте и измените свой код.

        //...
        .sessionManagement()
        .maximumSessions(1)
        .expiredUrl("/expired");
    }

to

        //...
        .sessionManagement().sessionFixation().newSession().maximumSessions(1)
        .expiredUrl("/expired")
        .sessionRegistry(sessionRegistry());
    }

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

В случае, если это не сработает, можете ли вы затем отправить код своих customLogoutHandler() и customLogoutSuccessHandler()? Вы используете Spring MVC вне Spring Boot, правильно?

Ответ 4

Если вы используете UserDetails и UserDetailsService, тогда это должно быть потому, что в вашем классе реализации UserDetails нет метода Override hashCode() и equals (Object obj). Это мой класс реализации для UserDetails:

public class MyUser implements UserDetails {
    private String username;

    private String password;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return null;
    }

    @Override
    public String getUsername() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }

    @Override
    public int hashCode() {
        return username.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return this.toString().equals(obj.toString());
    }
}