Java/Spring
Spring Security SessionManagementFilter, ConcurrentSessionFilter(동시세션제어)
B.OCK
2024. 9. 6. 11:06
반응형
SessionManagementFilter
세션 관리를 위한 필터로 Spring Security 에서 4가지 기능을 지원한다.
- 세션관리 : 인증 후 사용자의 세션 관리(등록, 조회, 삭제)
- 동시 세션 제어 : 동일 계정에 대한 세션 관리
- 세션 고정 보호 : 인증 후 세션 쿠키를 재발급하여 세션 쿠키 조작 공격을 방지
- 세션 생성 정책 : Always, If_Required, Never, Stateless
여기서 동시세션제어는 ConcurrentSessionFilter 가 담당한다.
ConcurrentSessionFilter
ConCurrentSessionFilter 는 SessionRegistry 를 이용하여 사용자의 로그인 동시세션 제어를 한다.
사용자 요청이 들어올 때마다 매번 세션이 만료(expired) 되었는지 체크하고 만료되었다면
해당 요청자를 로그아웃 시킨다.
로그아웃은 LogoutHandler 를 통해 인증객체가 저장되어 있는 SecurityContext 를 불러와
null 처리 한 후 SecurityContextRepository 에 저장하여 인증정보를 제거해버린다.
아래는 ConCurrentSessionFilter 의 doFilter 메서드이다.
public class ConcurrentSessionFilter extends GenericFilterBean {
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
private final SessionRegistry sessionRegistry;
private LogoutHandler handlers = new CompositeLogoutHandler(new SecurityContextLogoutHandler());
public ConcurrentSessionFilter(SessionRegistry sessionRegistry) {
Assert.notNull(sessionRegistry, "SessionRegistry required");
this.sessionRegistry = sessionRegistry;
this.sessionInformationExpiredStrategy = new ResponseBodySessionInformationExpiredStrategy();
}
...
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpSession session = request.getSession(false);
if (session != null) {
SessionInformation info = this.sessionRegistry.getSessionInformation(session.getId());
if (info != null) {
if (info.isExpired()) {
// Expired - abort processing
this.logger.debug(LogMessage
.of(() -> "Requested session ID " + request.getRequestedSessionId() + " has expired."));
doLogout(request, response);
this.sessionInformationExpiredStrategy
.onExpiredSessionDetected(new SessionInformationExpiredEvent(info, request, response));
return;
}
// Non-expired - update last request date/time
this.sessionRegistry.refreshLastRequest(info.getSessionId());
}
}
chain.doFilter(request, response);
}
}
SessionRegistry 를 이용하여 SessionRegistryImpl 에서 Session 정보를 불러온다.
불러온 Session 정보가 만료(isExpired) 되었는지 확인하고 만료되었다면 doLogout() 메서드를 통해
로그아웃 시킨다.
logout 메서드는 SecurityContextLogoutHandler 클래스에 존재한다.
public class SecurityContextLogoutHandler implements LogoutHandler {
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Assert.notNull(request, "HttpServletRequest required");
if (this.invalidateHttpSession) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Invalidated session %s", session.getId()));
}
}
}
SecurityContext context = this.securityContextHolderStrategy.getContext();
this.securityContextHolderStrategy.clearContext();
if (this.clearAuthentication) {
context.setAuthentication(null);
}
SecurityContext emptyContext = this.securityContextHolderStrategy.createEmptyContext();
this.securityContextRepository.saveContext(emptyContext, request, response);
}
}
SecurityContext 를 불러와 인증객체를 null 처리 하고 SecurityContextRepository 에 빈 SecurityContext 를 넣어
로그아웃 처리한다.
ConcurrentSessionFilter Flow
- 사용자 인증 요청이 들어온다.
- SessionManagementFilter 에서 SessionAuthenticationStrategy (세션 관리 전략) 중
ConcurrentSessionControlAuthenticationStrategy 에 의해 최대 세션 수를 체크하고
최대 세션수를 초과 했을때 이전 사용자 또는 로그인 한 사용자의 session 을 만료시킨다. - 이전 사용자 또는 만료된 사용자가 요청했을 때 ConcurrentSessionFilter 에 의해 세션 만료를 검사하고
만료 시 로그아웃 시킨다.
반응형