本文介绍了Spring Security maxSession不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要在用户超过maxSession计数时阻止登录.例如,每个用户只能登录一次.然后,如果登录的用户尝试使用其他登录系统,则应禁用该用户的登录.

I want to prevent login when user exceed maxSession count. For example every user can login once. And then if logged user try another login system should disable login for him.

.sessionManagement()
.maximumSessions(1).expiredUrl("/login?expire").maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry());


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

推荐答案

注意:已在 Spring MVC 4.3.9.RELEASE 上进行了测试em>,我还没用过Spring Boot.

NOTE: This is tested on Spring MVC and 4.3.9.RELEASE, I haven't used Spring Boot yet.

我找到了解决方案,让我分享一下它如何与我一起工作.

I found a solution, let me share how it worked with me.

1)我将HttpSecurity与SessionManagement进行了如下配置:

1) I configured HttpSecurity with SessionManagement as following:

@Override
protected void configure(HttpSecurity http) throws Exception {
  http
    .authorizeRequests()
      .antMatchers("/resources/**").permitAll()
      .antMatchers("/login**").permitAll()        // 1
      .antMatchers(...)
      .anyRequest().authenticated()
      .and()
    .formLogin()
      .loginPage("/login")
      .permitAll()
      .and()
    .logout()
      .deleteCookies("JSESSIONID")
      .permitAll()
      .and()
    .sessionManagement()                          // 2
      .maximumSessions(1)                         // 3
        .maxSessionsPreventsLogin(false)          // 4
        .expiredUrl("/login?expired")             // 5
        .sessionRegistry(getSessionRegistry())    // 6
    ;
}

借助文档 Spring Doc> HttpSecurity> sessionManagement()

以下配置演示了如何仅强制执行 一次验证用户的单个实例.如果是用户 使用用户名"user"进行身份验证而无需注销,并且 尝试通过用户"进行身份验证,将进行第一个会话 强制终止并发送到"/login?expired" URL.

The following configuration demonstrates how to enforce that only a single instance of a user is authenticated at a time. If a user authenticates with the username "user" without logging out and an attempt to authenticate with "user" is made the first session will be forcibly terminated and sent to the "/login?expired" URL.

@Configuration
@EnableWebSecurity
public class SessionManagementSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().hasRole("USER").and().formLogin()
                            .permitAll().and().sessionManagement().maximumSessions(1)
                            .expiredUrl("/login?expired");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
    }  }

在使用SessionManagementConfigurer.maximumSessions(int)时,请不要忘记 为应用程序配置HttpSessionEventPublisher以确保 过期的会话将被清除.在web.xml中,可以对其进行配置 使用以下内容:

When using SessionManagementConfigurer.maximumSessions(int), do not forget to configure HttpSessionEventPublisher for the application to ensure that expired sessions are cleaned up. In a web.xml this can be configured using the following:

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

或者, AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher() 可以返回true.

Alternatively, AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher() could return true.

我们可以知道为什么需要sessionManagement()maximumSessions(1),当然还需要expiredUrl("/login?expired").

We could know why we need sessionManagement(), maximumSessions(1), and of course expiredUrl("/login?expired").

  • 那么为什么需要antMatchers("/login**").permitAll()?这样您就可以被重定向到/login?expired,否则,您将被重定向到/login,因为在当前HttpSecurity配置下,permitAll()anyRequest().authenticated()应用于/login/login?logout.
  • So why the need of antMatchers("/login**").permitAll()?So that you could have permission to be redirect to /login?expired, otherwise you will be redirect to /login because anyRequest().authenticated(), with current HttpSecurity configuration permitAll() is applied to /login and /login?logout.

2)如果您实际上需要访问像我这样的特定用户的当前登录用户或expireNow()特定会话,则可能需要getSessionRegistry(),但没有它,maximumSessions(1)可以正常工作.

2) If you actually need to access current logged in user or expireNow() specific session for specific user like me, you might need getSessionRegistry(), but without it maximumSessions(1) works fine.

在文档的帮助下再次如此:

So again with the help of the doc:

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

或者, AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher() 可以返回true.

Alternatively, AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher() could return true.

所以我应该在我的SecurityWebInitializer.java类中更改我的替代enableHttpSessionEventPublisher():

So I should change my override enableHttpSessionEventPublisher() in my SecurityWebInitializer.java class:

public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
    @Override
    protected boolean enableHttpSessionEventPublisher() {
        return true;
    }
}

3)现在,我发现的最后一件事是我的问题: 当我刚接触Spring框架时,我学会了自定义UserDetails,但是实现起来有些不好,但是以后可能会做得更好,我创建了一个同时充当EntityUserDetails的实体:

3) Now just one last thing that I found it was my issue: As I am new in Spring framework, I learned to do custom UserDetails but with a little not good implementation, but I might do better later, I created an Entity that acts as both an Entity and a UserDetails:

    @Entity
    @Component("user")
    public class User implements UserDetails, Serializable {
        private static final long serialVersionUID = 1L;

        // ...

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof User) {
              return username.equals( ((User) obj).getUsername() );
            }
            return false;
        }

        @Override
        public int hashCode() {
            return username != null ? username.hashCode() : 0;
        }
    }

几年前,我在论坛此处,您应该同时实现两个hashCode() equals()方法,并且如果您查看UserDetails的默认实现的源代码 User.java 您会发现它同时实现了这两种方法,我做到了,并且它像一种魅力一样工作.

I found some one recommended years ago in the forum here, that you should implement both hashCode() equals() methods, and if you take look at the source code of the default implementation for UserDetails User.java you will find that it has both methods implemented, I did it and it worked like a charm.

就这样,希望对您有所帮助.

So that's it, hope this helpful.

您可能也想阅读此链接:春季-用户的所有会话都将过期

You might want to read this link too: Spring - Expiring all Sessions of a User

这篇关于Spring Security maxSession不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 06:57