
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
package com.takensoft.common.util;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author takensoft
* @since 2025.03.21
* @modification
* since | author | description
* 2025.03.21 | takensoft | 최초 등록
* 2025.05.29 | takensoft | Redis 통합 중복로그인 관리
* 2025.06.09 | takensoft | 세션 관리 로직 개선, 안정성 향상
*
* 세션 로그인 방식의 유틸리티
*/
@Slf4j
@Component
public class SessionUtil {
private final Map<String, HttpSession> sessionMap = new ConcurrentHashMap<>();
private final RedisTemplate<String, String> redisTemplate;
public SessionUtil(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 세션 등록 - 중복로그인 처리 개선
*/
public synchronized void registerSession(String mbrId, HttpSession newSession) {
// 1. 기존 세션 확인 및 무효화
HttpSession oldSession = sessionMap.get(mbrId);
if (oldSession != null && !oldSession.getId().equals(newSession.getId())) {
oldSession.invalidate();
}
// 2. 새 세션을 메모리에 등록
sessionMap.put(mbrId, newSession);
// 3. Redis에 세션 정보 저장 (세션 ID 저장)
String sessionKey = "session:" + mbrId;
redisTemplate.opsForValue().set(sessionKey, newSession.getId(),
Duration.ofSeconds(newSession.getMaxInactiveInterval()));
}
/**
* 세션 ID로 세션 무효화
*/
public void invalidateSessionById(String sessionId) {
// 메모리에서 해당 세션 ID를 가진 세션 찾아서 무효화
String targetMbrId = null;
sessionMap.entrySet().removeIf(entry -> {
HttpSession session = entry.getValue();
if (session != null && session.getId().equals(sessionId)) {
session.invalidate();
return true;
}
return false;
});
}
/**
* 사용자별 세션 제거 - Redis 연동 개선
*/
public void removeSession(String mbrId) {
// 1. 메모리 세션 무효화
HttpSession session = sessionMap.get(mbrId);
if (session != null) {
session.invalidate();
}
sessionMap.remove(mbrId);
// 2. Redis에서 모든 관련 키 제거
cleanupRedisSessionData(mbrId);
}
/**
* 전체 세션 무효화 - Redis 연동 개선
*/
public void invalidateAllSessions() {
// 1. 모든 메모리 세션 무효화
int invalidatedCount = 0;
for (Map.Entry<String, HttpSession> entry : sessionMap.entrySet()) {
HttpSession session = entry.getValue();
if (session != null) {
session.invalidate();
invalidatedCount++;
}
}
sessionMap.clear();
// 2. Redis에서 모든 세션 관련 키 삭제
cleanupAllRedisSessionData();
}
/**
* 특정 사용자의 Redis 세션 데이터 정리
*/
private void cleanupRedisSessionData(String mbrId) {
// 세션 관련 키들 삭제
String sessionKey = "session:" + mbrId;
String sessionTokenKey = "session_token:" + mbrId;
redisTemplate.delete(sessionKey);
redisTemplate.delete(sessionTokenKey);
// 패턴 매칭으로 사용자 관련 모든 세션 키 찾아서 삭제
Set<String> userSessionKeys = redisTemplate.keys("session*:" + mbrId);
if (userSessionKeys != null && !userSessionKeys.isEmpty()) {
redisTemplate.delete(userSessionKeys);
}
}
/**
* 모든 Redis 세션 데이터 정리
*/
private void cleanupAllRedisSessionData() {
// 모든 세션 관련 키 패턴들
String[] sessionKeyPatterns = {
"session:*",
"session_token:*"
};
for (String pattern : sessionKeyPatterns) {
Set<String> keys = redisTemplate.keys(pattern);
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
}
/**
* 활성 세션 수 조회
*/
public int getActiveSessionCount() {
return sessionMap.size();
}
/**
* 특정 사용자의 세션 활성 상태 확인
*/
public boolean isSessionActive(String mbrId) {
HttpSession session = sessionMap.get(mbrId);
if (session == null) {
return false;
}
try {
// 세션 접근을 통한 유효성 확인
session.getLastAccessedTime();
return true;
} catch (IllegalStateException e) {
// 무효화된 세션이면 맵에서 제거
sessionMap.remove(mbrId);
return false;
}
}
}