상세 컨텐츠

본문 제목

[SPRING SECURITY] ConcurrentSessionFilter 이용하여 동시접속 제어하기

JAVA/SPRING

by ranlan 2022. 11. 20. 22:47

본문

728x90

최근까지 하던 업무 중 동시접속에 대한 제어 기능 추가가 있었다.

스프링 시큐리티인듯 아닌듯한 프레임워크이기에 스프링 시큐리티 필터와 직접 SessionRegistry를 이용하여 세션 정보를 이용하는 방법 두 가지를 모두 이용하였다.

 

** 스프링 부트 프로젝트도 아니고 시큐리티 관련 설정을 모두 xml 파일에서 했기에 그에 맞춰 정리해본다.

 

먼저 📄 web.xml에 세션 처리 관련 리스너를 추가한다.

<listener>
    <listener-class>
        org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

다음으로 security 관련 설정 xml 파일에 아래 내용을 추가하면 된다고는 하는데

<http>
	...
    <session-management>
        <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
    </session-management>
    ...
</http>

나의 경우 자사 프레임워크의 특징인지 뭔지 안먹혀서 이 방법은 안썼다.

 

그 대신 동시성 제어 기능을 제공하는 SessionAuthenticationStrategy를 추가하였다.

* SessionAuthenticationStrategy는 인터페이스이고 이를 구현한 것이 CompositeSessionAuthenticationStrategy

<http>
    <!-- 여러 커스텀 필터들 뒤 마지막에 추가 -->
    <session-management session-authentication-strategy-ref="sas"/>
</http>

<!-- 커스텀 필터 -->
<beans:bean id="myAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <!-- SessionAuthenticationStrategy 추가 -->
    <beans:property name="sessionAuthenticationStrategy" ref="sas" />
    ...
    <beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>

<beans:bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<beans:constructor-arg>
    <beans:list>
    <beans:bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
        <beans:constructor-arg ref="sessionRegistry"/>
        <beans:property name="maximumSessions" value="1" />
        <beans:property name="exceptionIfMaximumExceeded" value="true" />
    </beans:bean>
    <beans:bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
    </beans:bean>
    <beans:bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
        <beans:constructor-arg ref="sessionRegistry"/>
    </beans:bean>
    </beans:list>
</beans:constructor-arg>
</beans:bean>

<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
  • maximumSessions
    동시 접속 가능한 최대 세션 허용 수이다. 1인 경우 세션 한개만 허용임으로 동시 접속이 불가능하다.
  • exceptionIfMaximumExceeded
    • true인 경우 허용 가능한 세션 수가 넘어가면 로그인이 불가능하다. 즉 두번째 사용자가 로그인하지 못한다.
    • false인 경우 두번째 세션이 허용되고 이전 세션이 만료된다.
      하지만 이 경우 만료된 세션에 대한 처리(물리적인 세션 만료)가 필요함으로 ConcurrentSessionFilter가 필요하다.

 

exceptionIfMaximumExceeded 옵션을 통해 간단하게 두번째 로그인을 차단할 수 있지만 내 요구사항의 경우 이전 로그인을 만료시켜야했다.

따라서 앞선 설명처럼 ConcurrentSessionFilter도 추가해주었다.

<http>
    <!-- 다른 커스텀 필터들 ... -->
	<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
    
    <session-management session-authentication-strategy-ref="sas"/>
</http>

...

<!-- ConcurrentySessionFilter 추가 -->
<beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <beans:constructor-arg name="sessionInformationExpiredStrategy" ref="CustomSessionInfoExpiredStrategy" />
</beans:bean>
  • SessionRegistry는 아까 위에서 주입해준 SessionRegistry
  • sessionInformationExpiredStrategy는 인터페이스로 직접 커스텀한 클래스 생성 후 주입해준다.
<beans:bean id="CustomSessionInfoExpiredStrategy" class="{class경로}.CustomSessionInfoExpiredStrategy"/>

해당 클래스에서는 expired 여부가 true인 세션에 대해 처리 방안을 구현한다.

public class CustomSessionInfoExpiredStrategy implements SessionInformationExpiredStrategy {

    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
        // 만료된 세션 처리
    }
}

 

** 여기서 주의해야할 점은 빈 속성으로 expiredUrl이 아닌 sessioinInformatioinExpriedStrategy를 지정해줘야한다는 것!!

[Spring Security 4.2 sessionInformationExpiredStrategy 사용] https://underbell.tistory.com/90

 

Spring Security 4.2 sessionInformationExpiredStrategy 사용

Spring Security 4.2 에서 ConcurrentSessionFilter 사용 시 expiredUrl 사용이 Deprecated 되었습니다. @Deprecated public ConcurrentSessionFilter(SessionRegistry sessionRegistry, String expiredUrl) { expiredUrl 대신 SessionInformationExpiredStrat

underbell.tistory.com

간혹 구글링하다보면 ConcurrentSessionFilter 주입하는 부분이 아래처럼 작성된 코드들이 있다.

<beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <beans:property name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="expiredUrl" value="/logout" />
</beans:bean>

하지만 이렇게 작성하면 expiredUrl이 읽히지않아 NullPointerException으로 오류뜨면서 이전 사용자가 제대로 로그아웃되지 않는다ㅠ(이 에러 원인 찾는데 거의 며칠을 보낸듯)

 

그리고 저 bean 주입할 때 property가 아니라 construtor-arg로 해라 뭐 이런글도 봤던거같은데 기억이 잘 안난다..(찾아보고 추가예정)

[다양한 의존객체 주입 (constructor-arg, property)] https://junior-datalist.tistory.com/36

 

다양한 의존객체 주입 (constructor-arg, property)

다양한 의존 객체 주입 방법 bean 객체 내에 의존객체를 주입하는 대표적인 방법은 생성자(constructor) 주입 기본자료형(property) 주입 리스트 주입 해쉬맵 주입 4가지가 있습니다. 그중에서 constructor-

junior-datalist.tistory.com

 

뭐 이런저런 우여곡절끝에 동시접속 차단 요구사항 해결 👍🏻

 

 


[토리맘의 한글라이즈 프로젝트 | Spring Security Authentication] https://godekdls.github.io/Spring%20Security/authentication/

728x90

관련글 더보기

댓글 영역