스프링시큐리티 중복로그인(동시접속) 제한처리 - 다중서버환경

2018. 11. 28. 15:03 Spring Framework/Spring Core

스프링시큐리티에서 중복로그인 제한은 사실 간단하게 처리할 수 있다.


security-context.xml


<http>

    <session-management>

        <concurrency-control max-sessions="1"></concurrency-control>

    </session-management>

</http>


위 코드처럼 간단히 max-sessions="1" 속성으로 동접을 제한할 수 있다.








하지만, WAS서버가 단일구성이 아니라면 

WEB서버 설정에 따라 다른 서버로 세션이 붙게 되면 동접이 가능해지게 된다.


다른전략으로 접근을 해야했다.


로그인은 AuthenticationSuccessHandler 핸들러를 등록하여 

사용자가 로그인을 성공할 시 DB에 사용자로그인 상태를 INSERT하였고



로그아웃은 ApplicationListener의 구현체를 등록하여 

사용자의 세션종료 브로드캐스팅을 캐치하여 사용자로그인정보를 DELETE하였다.


security-context.xml

<http>

...

    <form-login login-page="/common/auth/denied" authentication-success-handler-ref="customAuthenticationSuccessHandler" />

...

</http>


CustomAuthenticationSuccessHandler.java

import java.io.IOException;

 

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.springframework.security.core.Authentication;

import org.springframework.security.core.userdetails.User;

import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;

import org.springframework.stereotype.Component;

 

@Component

public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

 

     

    @Override

    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response

            , Authentication authentication) throws IOException, ServletException {

         

        // 로그인성공한 유저정보

        User user = (User) authentication.getPrincipal();

         

        // 세션아이디

        String sessionId = request.getSession().getId();

         

        // 로그인정보 DB insert처리

         

        super.onAuthenticationSuccess(request, response, authentication);

    }

}


SessionDestoryListener.java

import java.util.List;

 

import org.springframework.context.ApplicationListener;

import org.springframework.security.core.context.SecurityContext;

import org.springframework.security.core.session.SessionDestroyedEvent;

import org.springframework.security.core.userdetails.User;

import org.springframework.stereotype.Component;

 

@Component

public class SessionDestoryListener implements ApplicationListener<sessiondestroyedevent> {

 

    @Override

    public void onApplicationEvent(SessionDestroyedEvent event) {

 

        List<securitycontext> contexts = event.getSecurityContexts();

        if (!contexts.isEmpty()) {

            for (SecurityContext ctx : contexts) {

                // 로그아웃 된 유저정보

                User cu = (User) ctx.getAuthentication().getPrincipal();

                 

                // 로그아웃  DB delete처리

            }

        }

 

    }

}


그 다음은 자유롭게


로그인 페이지에서 DB에 해당 사용자의 세션이 존재하고 있는지 체크한 후


"접속중인 사용자가 있습니다. 연결을 끊고 로그인 하시겠습니까? 라는


간단한 화면처리나 alert메시지 처리를 해주면 된다.


단! 서버 재기동 시 SessionDestoryListener이벤트를 받지 못하기 때문에


사용자가 재로그인 시도 시 DB에 로그인정보가 있기 때문에 주의하여야 한다.


물론 세션클러스터링이 잘되고 있는 환경이라면 크게 문제는 없을 것 같다.


(정책적으로 기존로그인 사용자는 튕겨내는 방식을 추천)


다중서버환경에서는 근본적으로 Redis같은 캐시형 DB로 세션관리하는게 


제일 바람직하지 않을까 생각해본다.



출처: http://nuridan.tistory.com/1 [숙취의 개발노트]