fat*_*dem 5 spring spring-security spring-session
当用户超过maxSession计数时,我想防止登录。例如,每个用户只能登录一次。然后,如果登录的用户尝试使用其他登录系统,则应禁用该用户的登录。
.sessionManagement()
.maximumSessions(1).expiredUrl("/login?expire").maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry());
@Bean
public static ServletListenerRegistrationBean httpSessionEventPublisher() {
return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
}
Run Code Online (Sandbox Code Playgroud)
小智 6
我遇到了同样的问题,它源于我的 UserDetails 实现:
ConcurrentSessionControlAuthenticationStrategy 第 93 行:
final List<SessionInformation> sessions = sessionRegistry.getAllSessions(
authentication.getPrincipal(), false);
Run Code Online (Sandbox Code Playgroud)
SessionRegistryImpl 第 64 行:
final Set<String> sessionsUsedByPrincipal = principals.get(principal);
if (sessionsUsedByPrincipal == null) {
return Collections.emptyList();
}
Run Code Online (Sandbox Code Playgroud)
在会话注册表中,在“主体”列表中搜索 UserDetails 对象。因此,您需要在 UserDetails 实现中重写equals和hashcode,否则,它将把它们视为单独的对象,因此始终返回一个空列表。
例子:
public class ApplicationUser implements UserDetails {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ApplicationUser)) return false;
ApplicationUser that = (ApplicationUser) o;
return username.equals(that.username) &&
email.equals(that.email) &&
password.equals(that.password);
}
@Override
public int hashCode() {
return Objects.hash(username, email, password);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:这已经在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
;
}
Run Code Online (Sandbox Code Playgroud)
借助文档Spring Doc> HttpSecurity> sessionManagement()
配置示例
以下配置演示了如何强制一次仅认证一个用户实例。如果用户未注销而使用用户名“ user”进行身份验证,并且尝试使用“ user”进行身份验证,则第一个会话将被强制终止并发送到“ / login?expired” URL。
Run Code Online (Sandbox Code Playgroud)@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中,可以使用以下命令进行配置:
Run Code Online (Sandbox Code Playgroud)<listener> <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> </listener>或者,AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher()可以返回true。
我们可以知道为什么我们需要sessionManagement(),maximumSessions(1),当然expiredUrl("/login?expired")。
antMatchers("/login**").permitAll()?这样您就可以被重定向到/login?expired,否则您将被重定向到,/login因为anyRequest().authenticated(),将当前HttpSecurity配置permitAll()应用于/login和/login?logout。2)如果您实际上需要访问expireNow()像我这样的特定用户的当前登录用户或特定会话,则可能需要getSessionRegistry(),但如果不能maximumSessions(1)正常工作。
因此,再次在文档的帮助下:
使用SessionManagementConfigurer.maximumSessions(int)时,请不要忘记为应用程序配置HttpSessionEventPublisher,以确保清除过期的会话。在web.xml中,可以使用以下命令进行配置:
Run Code Online (Sandbox Code Playgroud)<listener> <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> </listener>或者,AbstractSecurityWebApplicationInitializer.enableHttpSessionEventPublisher()可以返回true。
因此,我应该enableHttpSessionEventPublisher()在SecurityWebInitializer.java课堂上更改我的替代:
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
@Override
protected boolean enableHttpSessionEventPublisher() {
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
3)现在,我发现的最后一件事是我的问题:由于我是Spring框架的新手,我学会了自定义UserDetails,但是实现起来有些差,但是以后可能会做得更好,我创建了一个实体作为an Entity和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;
}
}
Run Code Online (Sandbox Code Playgroud)
我发现有人建议多年前的论坛在这里,你应该同时实现hashCode() equals()的方法,如果你在对的UserDetails默认实现的源代码,您或许User.java你会发现,它已经实施了这两种方法,我做到了,它的工作像个魅力。
就是这样,希望对您有所帮助。
您可能也想阅读此链接:Spring-用户的所有会话期满
| 归档时间: |
|
| 查看次数: |
2107 次 |
| 最近记录: |