주요글: 도커 시작하기
반응형
3부에 이어 계속해서 접속 중인 사용자 리스트의 실질적인 구현 방법을 알아본다.

다중 접속에 관하여

다중 접속을 허용하는가 금지하는가는 서버 운영자가 선택하여야 한다. 지금까지의 코드로는 여러 곳에서 같은 아이디로 접속하는 것을 허용하고 있다. 만약에 다중 접속을 막고 싶으면 어떻게 해야 할까.

몇가지 방법을 생각해볼 수 있다. 일단 접속 중인 사용자 정보를 어디에서부터 확인할 수 있을까. 특별하게 엄청난 코드를 추가하지 않아도 두 곳에 사용자 정보가 이미 저장되고 있다는 사실을 알 수 있다.

데이터베이스와 LoginUserList 객체

첫번째는 데이터베이스에 로그인 중이라는 상태를 나타내는 필드를 추가하는 방법이 있을 수 있다. MemberManager 클래스의 login() 메소드가 호출될 때 자동으로 이 필드를 true 또는 1 등의 값으로 설정하여 로그인 상태라는 것을 표시한다. 그리고 logout() 메소드가 호출될 때 이 필드를 false 또는 0 등의 값으로 설정하여 로그아웃 상태라는 것을 표시한다.

두번째는 접속 중인 사용자 리스트를 유지하고 있는 LoginUserList 클래스로부터 로그인 상태를 확인하는 방법이다. 이미 로그인 중인 사용자 리스트를 관리하는 클래스를 사용한다면 별로도 데이터베이스로부터 로그인 상태를 확인하기 위한 작업을 할 필요가 없다. 따라서 LoginUserList 클래스로부터 접속 중인지 아닌지 확인하는 방법을 찾아보자.

isLoginUser() 메소드

이미 LoginUserList 클래스에는 isLoginUser() 메소드가 있다. 또한 MemberManager 클래스에도 이 메소드를 호출할 수 있는 메소드가 똑같이 존재한다. 그렇다면 login() 메소드에서 로그인을 시도할 때 isLoginUser() 메소드를 호출하여 접속 중인지 간단히 미리 확인할 수 있다.

이 때, 시나리오를 먼저 생각해볼 필요가 있다. 로그인을 시도했을 때 이미 접속 중일 경우

  1. 기존의 접속을 강제로 끊는다.
  2. 기존의 접속을 유지하고 로그인을 거부한다.
  3. 선택할 수 있도록 한다.
중요하진 않지만 언제나 이런 상황에서는 실제 시나리오를 생각해볼 필요가 있다. 이 경우는 기존의 접속을 강제로 끊기로 한다.

그러나, 실제적인 문제는 이제부터 발생한다. 로그인/로그아웃 정보는 세션에서 유지한다. 다른 곳에서 로그인한 경우, 그 로그인 정보는 해당 세션 객체에서 유지되므로, 그 세션을 접근할 수 있어야 한다. 그러나, 지금까지의 코드로는 다른 세션을 접근할 방법이 없다.

어떤 사람은 HttpSessionContext 클래스를 이용하면 될 것이라고 생각할지 모르지만 이 클래스는 서블릿 2.1 규약에서부터 보안상의 이유로 디프리케이티드되었다. 따라서 이 클래스는 사용하지 않는 것이 옳다.

LoginBean 인터페이스와 LoginBeanImpl 클래스의 수정

가장 쉬운 방법은 LoginBean 클래스가 스스로 세션에 대한 레퍼런스를 보관하는 방법이다. 간단히 LoginBean 인터페이스와 LoginBeanImpl 클래스를 수정한다. LoginBean 인터페이스에는 다음과 같은 메소드를 추가한다.

   public HttpSession getSession();

그리고, LoginBeanImpl 클래스는 LoginBean 인터페이스를 상속하므로 당연히 getSession() 메소드를 구현하기 위하여 다음과 같은 코드를 추가한다.

   private HttpSession session = null;

   public HttpSession getSession() {
      return session;
   }

LoginBeanImpl 클래스에서 getSession() 메소드의 session 값은 어디서 설정해야 할까. 당연히 valueBound() 메소드이다. valueBound() 메소드에는 다음과 같은 코드가 추가되야 한다.

   session = event.getSession();

또한, 같은 이유로 valueUnbound() 메소드에는 다음과 같은 코드가 추가된다.

   session = null;

결과적으로 LoginBeanImpl 클래스의 완전한 소스는 다음과 같다.

   public class LoginBeanImpl implements LoginBean, HttpSessionBindingListener {
      
      private LoginBeanBindingListener listener = null;
      private String id = null;
      private HttpSession session = null;
      
      public LoginBeanImpl(String id, LoginBeanBindingListener listener) {
         this.id = id;
         this.listener = listener;
      }
      
      public String getId() {
         return id;
      }
   
      public HttpSession getSession() {
         return session;
      }
   
      public void valueBound(HttpSessionBindingEvent event) {
         session = event.getSession();
         if (listener != null) 
            listener.loginPerformed(new LoginBeanBindingEvent(this));
      }
   
      public void valueUnbound(HttpSessionBindingEvent event) {
         session = null;
         if (listener != null) 
            listener.logoutPerformed(new LoginBeanBindingEvent(this));
      }
   }

LoginUserList 클래스의 수정

다음은 LoginUserList 클래스에서 주어진 아이디에 해당하는 LoginBean 객체를 얻을 수 있는 getLoginBean() 메소드를 추가하면 된다. getLoginBean() 메소드의 코드는 다음과 같다.

   public LoginBean getLoginBean(String id) {
      try {
         return (LoginBean)users.get(id);
      } catch (Exception ex) {
         return null;
      }
   }

MemberManager 클래스의 login() 메소드의 수정

이제 마지막으로 MemberManager 클래스의 login() 메소드를 수정한다.

   public void login(HttpSession session, String id, String password)
               throws NoSuchMemberException,
                      InvalidPasswordException,
                      ServiceNotActiveException {
      accessor.checkPassword(id, password);
      // 다중 접속을 막기 위해 추가된 if 문장 
      if (isLoginUser(id)) {
         LoginBean lb = users.getLoginBean(id);
         lb.getSession().removeAttribute(LOGIN_BEAN);
      }
      LoginBean login = new LoginBeanImpl(id, this);
      session.setAttribute(LOGIN_BEAN, login);
   }

이제 로그인 중인 사용자가 다른 곳에서 같은 아이디로 정상적으로 접속할 경우 이미 로그인한 세션에 대해 자동적으로 로그아웃을 시키게 된다.

결론

Part IV 에서는 접속 중인 사용자가 다른 곳에서 다중으로 로그인 하는 것을 막는 방법을 제시해보았다. 이로서 4회에 걸쳐 로그인과 로그아웃의 기본적인 구현 알고리즘과 개념, 세션 타임 아웃으로 인한 로그아웃의 구현, 커플링(coupling)을 제거하기 위한 방법, 로그인 유저 리스트의 구현과 접근에 유지 방법, 그리고 다중 접속을 막는 방법 등을 실제로 구현하여보았다.



본 글의 저작권은 이동훈에 있으며 저작권자의 허락없이 온라인/오프라인으로 본 글을 유보/복사하는 것을 금합니다.

+ Recent posts