Spring Security maxSession 不起作用 [英] Spring Security maxSession doesn't work

查看:77
本文介绍了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 MVC4.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) 我使用 SessionManagement 配置了 HttpSecurity,如下所示:

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 因为 anyRequest().authenticated()code>,使用当前的 HttpSecurity 配置 permitAll() 应用于 /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.

再次在文档的帮助下:

在使用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.

所以我应该在我的 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,但实现起来有点不太好,但我以后可能会做得更好,我创建了一个同时充当 Entity 的实体>用户详细信息:

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.

您可能也想阅读此链接:Spring - 使用户的所有会话过期

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

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆