Spring Security 自动踢掉前一个登录用户的实现代码
时间: 2024-05-06 14:15:12 浏览: 8
实现自动踢掉前一个登录用户,可以通过监听 SessionDestroyedEvent 事件,在用户注销或 Session 超时时触发事件,将前一个登录用户踢出系统。以下是 Spring Security 自动踢掉前一个登录用户的实现代码:
1. 自定义 SessionRegistryImpl 类,继承 Spring Security 提供的 ConcurrentMapSessionRegistry 类,并实现 SessionRegistry 接口,用于存储用户 Session 信息。
```java
@Component
public class SessionRegistryImpl extends ConcurrentMapSessionRegistry implements SessionRegistry {
private final Logger logger = LoggerFactory.getLogger(SessionRegistryImpl.class);
private final Map<String, String> principalSessionIds = new ConcurrentHashMap<>();
private final Set<String> kickedOutSessionIds = new HashSet<>();
@Override
public void registerNewSession(String sessionId, Object principal) {
super.registerNewSession(sessionId, principal);
if (principal != null) {
String username = ((UserDetails) principal).getUsername();
logger.debug("registerNewSession: username={}, sessionId={}", username, sessionId);
principalSessionIds.put(username, sessionId);
}
}
@Override
public void removeSessionInformation(String sessionId) {
SessionInformation sessionInformation = getSessionInformation(sessionId);
if (sessionInformation != null) {
Object principal = sessionInformation.getPrincipal();
if (principal != null) {
String username = ((UserDetails) principal).getUsername();
logger.debug("removeSessionInformation: username={}, sessionId={}", username, sessionId);
principalSessionIds.remove(username, sessionId);
}
}
kickedOutSessionIds.remove(sessionId);
super.removeSessionInformation(sessionId);
}
@Override
public void onApplicationEvent(SessionDestroyedEvent event) {
String sessionId = event.getId();
SessionInformation sessionInformation = getSessionInformation(sessionId);
if (sessionInformation != null) {
Object principal = sessionInformation.getPrincipal();
if (principal != null) {
String username = ((UserDetails) principal).getUsername();
logger.debug("onApplicationEvent: username={}, sessionId={}", username, sessionId);
String kickedOutSessionId = principalSessionIds.get(username);
if (!sessionId.equals(kickedOutSessionId)) {
kickedOutSessionIds.add(kickedOutSessionId);
}
}
}
super.onApplicationEvent(event);
}
public void removeKickedOutSession(String sessionId) {
kickedOutSessionIds.remove(sessionId);
}
public Set<String> getKickedOutSessionIds() {
return Collections.unmodifiableSet(kickedOutSessionIds);
}
}
```
2. 自定义 SessionManagementFilter 类,继承 Spring Security 提供的 SessionManagementFilter 类,并在 doFilterInternal 方法中实现自动踢掉前一个登录用户的逻辑。
```java
@Component
public class SessionManagementFilterImpl extends SessionManagementFilter {
private final Logger logger = LoggerFactory.getLogger(SessionManagementFilterImpl.class);
private final SessionRegistry sessionRegistry;
public SessionManagementFilterImpl(SessionRegistry sessionRegistry) {
super(sessionRegistry);
this.sessionRegistry = sessionRegistry;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpSession session = request.getSession(false);
if (session != null) {
SessionInformation sessionInformation = sessionRegistry.getSessionInformation(session.getId());
if (sessionInformation != null && sessionInformation.isExpired()) {
logger.debug("doFilterInternal: session expired, sessionId={}", session.getId());
sessionRegistry.removeSessionInformation(session.getId());
session = request.getSession();
}
}
super.doFilterInternal(request, response, chain);
if (session != null) {
SessionInformation sessionInformation = sessionRegistry.getSessionInformation(session.getId());
if (sessionInformation != null && sessionInformation.isExpired()) {
logger.debug("doFilterInternal: session expired after super.doFilterInternal, sessionId={}", session.getId());
sessionRegistry.removeSessionInformation(session.getId());
}
}
Set<String> kickedOutSessionIds = sessionRegistry.getKickedOutSessionIds();
if (!kickedOutSessionIds.isEmpty()) {
for (String sessionId : kickedOutSessionIds) {
HttpSession kickedOutSession = request.getSession(false);
if (kickedOutSession != null && sessionId.equals(kickedOutSession.getId())) {
logger.debug("doFilterInternal: kick out session, sessionId={}", sessionId);
kickedOutSession.invalidate();
}
sessionRegistry.removeKickedOutSession(sessionId);
}
}
}
}
```
3. 在 Spring Security 配置中注入自定义 SessionRegistryImpl 和 SessionManagementFilterImpl。
```java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final SessionRegistry sessionRegistry;
private final SessionManagementFilter sessionManagementFilter;
public SecurityConfig(SessionRegistry sessionRegistry, SessionManagementFilter sessionManagementFilter) {
this.sessionRegistry = sessionRegistry;
this.sessionManagementFilter = sessionManagementFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic()
.and()
.sessionManagement()
.maximumSessions(1)
.sessionRegistry(sessionRegistry)
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.addFilterBefore(sessionManagementFilter, ChannelProcessingFilter.class);
}
}
```