我想在用户超过 maxSession 计数时阻止登录。例如,每个用户可以登录一次。然后如果登录的用户尝试另一个登录系统应该禁用他的登录。
.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上测试过的,我还没用过Spring Boot。
我找到了一个解决方案,让我分享一下它是如何工作的。
1) 我使用 SessionManagement 配置了 HttpSecurity,如下所示:
@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()
Example Configuration
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"); } }
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>
Alternatively, AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher() could return true.
我们可以知道为什么我们需要
sessionManagement()
, maximumSessions(1)
,当然还有 expiredUrl("/login?expired")
.antMatchers("/login**").permitAll()
?这样你就可以有权限被重定向到
/login?expired
, 否则你将被重定向到 /login
因为 anyRequest().authenticated()
, 与电流 HttpSecurity
配置permitAll()
适用于/login
和 /login?logout
. 2) 如果您确实需要访问当前登录的用户或
expireNow()
像我这样的特定用户的特定 session ,您可能需要 getSessionRegistry()
,但没有它 maximumSessions(1)
工作正常。所以再次在文档的帮助下:
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>
Alternatively, AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher() could return true.
所以我应该改变我的覆盖
enableHttpSessionEventPublisher()
在我的 SecurityWebInitializer.java
类(class):public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
@Override
protected boolean enableHttpSessionEventPublisher() {
return true;
}
}
3)现在我发现的最后一件事这是我的问题 :
由于我是 Spring 框架的新手,我学会了自定义 UserDetails,但实现起来不太好,但我以后可能会做得更好,我创建了一个同时充当
Entity
的实体。和一个 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;
}
}
我在论坛里找到了一些多年前推荐的here ,您应该同时实现
hashCode()
equals()
方法,如果您查看 UserDetails User.java
的默认实现的源代码你会发现它同时实现了这两种方法,我做到了,而且效果很好。就是这样,希望这有帮助。
您可能也想阅读此链接:Spring - Expiring all Sessions of a User
关于Spring Security maxSession 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37892563/