Backend/SpringBoot

[Spring Security] SessionID 관련 주요 필터, 이벤트처리 및 설정

Dean83 2025. 12. 17. 09:41

SessionManagementFilter

  • 동시 세션 제어 : 동일 계정의 최대 허용 세션 수 초과여부 확인 및 처리
  • 세션 보호 : 로그인시 새로운 세션 생성하여 기존 세션 사용 못하도록 함
  • 세션 생정시 이벤트 발생

 

ConcurrentSessionFilter

  • 매 요청마다 세션의 만료 여부를 실시간으로 체크하여 중복로그인 등으로 만료된 세션 접근 차단

 

HttpSessionEventPublisher

  • 서블릿 컨테이너(예 : 톰캣) 에서 발생하는 HttpSession 이벤트 감지하여 Spring 이벤트로 변환 하고 발행함
  • Spring 에서는 서블릿 컨테이너 동작을 알 수 없기 때문에 중간에 다리를 놓아주는 게 필요하다.
  • 동시세션 제어 기능을 정확하게 동작시키기 위해 반드시 필요.
  • SessionCreationEvent
  • SessionDestroyedEvent

 

SessionInformationExpiredStrategy

  • 세션이 만료 되었을때 클라이언트에 응답을 하기 위해 사용. 이벤트를 감지하여 동작하므로 HttpSessionEventPublisher가 반드시 필요하다.
  • 세션이 있었으나 만료 되었을 경우에 해당한다.

InvalidSessionStrategy

  • 세션이 존재하지 않거나 없을 경우 처리 한다. 위의 SessionInformationExpiredStrategy 와 비슷하나 의미가 다르다. 

 

SessionRegistry

  • 현재 로그인 사용자의 세션을 추척하는 저장소로, 사용자가 몇개의 세션을 가지고 있는지, 유효한지 등을 판단할때 사용함.
  • HttpSessionEventPublisher 가 반드시 필요하다. 여기서 발생한 이벤트를 이용한다.
  • Spring 에서 저장하는 부분
    • 톰캣 같은 서블릿 컨테이너에서 세션아이디를 별도로 관리하나, SessionId를 key로 하여 map에서 관리 하기 때문에 비즈니스 로직 (예 : A 사용자가 가진 세션ID) 관점에서 세션아이디를 찾거나 할 수 없어 따로 스프링에서 저장하여 활용함.

 

SecurityFilterChain 에 다음과 같이 설정 할 수 있다.

...

@Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(x -> x.anyRequest()
        .permitAll()
    )
    .....
    .sessionManagement(session ->
                         session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                        .invalidSessionStrategy(invalidSessionStrategy())
                        .maximumSessions(1)
                        .maxSessionsPreventsLogin(true)
                        .sessionRegistry(sessionRegistry())
                        .expiredSessionStrategy(sessionInformationExpiredStrategy())
                        

    return http.build();
  }
  
  ...
  
  @Bean
  public SessionRegistry sessionRegistry() {
      return new SessionRegistryImpl();
  }

  @Bean
  public HttpSessionEventPublisher httpSessionEventPublisher() {
      return new HttpSessionEventPublisher();
  }
  
  @Bean
  public SessionInformationExpiredStrategy sessionInformationExpiredStrategy() {
     return event -> {
          HttpServletResponse response = event.getResponse();

          response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
          response.setContentType("application/json;charset=UTF-8");

          response.getWriter().write("""
            {
              "code": "SESSION_EXPIRED",
              "message": "다른 곳에서 로그인되어 세션이 만료되었습니다."
            }
        """);
      };
    }
    
    @Bean
    public InvalidSessionStrategy invalidSessionStrategy() {
      return new InvalidSessionStrategy() {
          @Override
          public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
              response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
              response.setContentType("application/json;charset=UTF-8");

              response.getWriter().write("""
            {
              "code": "SESSION_EXPIRED",
              "message": "만료된 세션입니다."
            }
        """);
          }
      };
    }
  }

 

SessionManagement 옵션 설명

  • sessionCreationPolicy
    • IF_REQUIRED : 기본값. 필요시 생성
    • STATELESS : JWT 이용시 설정. 
    • NEVER : 사용치 않음
    • ALWAYS : 항상 사용
  • maxSessionPreventsLogin : 최대 세션 도달시 로그인 허용 여부
    • false일 경우 기존 로그인 세션은 풀리고 새 로그인 세션 유지
    • true일 경우 새 로그인이 안된다.
  • maxSessions : 동시 최대 세션 수
  • expiredSessionStrategy : 세션이 만료 되었을때 클라이언트에 응답하기 위한 부분
  • invalidSessionStrategy : 세션이 없을경우, 틀릴경우 응답하기 위한 부분

 

세션의 타임아웃을 설정하기 위해 applicaion.yaml 에서 설정해 준다. 

server:
 servlet:
  session:
   timeout: 30m

 

세션은, 쿠키에 자동으로 추가되므로, 쿠키 관련 설정도 해주어야 한다. 이 페이지(https://dean83.tistory.com/378) 에서 쿠키를 수동으로 발급 했을 때 에는 각종 설정들을 코드로 할 수 있었으나, 세션의 경우 자동으로 쿠키를 쓰다보니, 설정을 applicaion.yaml 에 추가 해야 한다.

 

server:
  servlet:
    session:
      cookie:
        http-only: true
        secure: true
        same-site: none