Spring Security maxSession 不起作用 [英] Spring Security maxSession doesn't work
问题描述
我想在用户超过 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) 我使用 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
becauseanyRequest().authenticated()
, with currentHttpSecurity
configurationpermitAll()
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屋!