본문 바로가기
  • 오늘도 한걸음. 수고많았어요.^^
  • 조금씩 꾸준히 오래 가자.ㅎ
IT기술/JAVA

중복로그인 체크/ 차단하기, 세션 기존접속 끊기

by 미노드 2017. 3. 22.

/* 
* 로그인 중복체크
* 이미 로그인한 사용자가 있을경우 기존의 사용자 세션을 종료후 자신이 로그인.
* 해시테이블에 세션과 접속자 아이디를 저장해 둔다.
* 세션 Object를 저장하는 이유는 동일한 아이디로 재접속 했을경우 
* 아이디로 세션Object를 찾아내어 기존의 접속을 끊기위해서다.(invalidate)


* 거의 대부분의 웹사이트를 보면 브라우저를 열고 로그인후
* 또다시 다른브라우저를 열고 로그인을 하면 로그인이 되는것을 확인할 수 있다.
* 이는 즉... 여러곳에서 동일한 아이디로 접속을 할수있다는 것이다.
* 이와 반대로 메신저같은경우는 이미 로그인이 되어있을시 다른곳에서
* 로그인을 하면 접속을 끊을지를 물어보는 기능도 확인할 수 있는데. 이를 웹사이트에서도 구현하는 것이다.
*/


servlet을 이용함


============== WEB-INF\src\test\LoginManager.java ===============
package test;

import java.util.*;
import javax.servlet.http.*;
/*
* session이 끊어졌을때를 처리하기 위해 사용
* static메소드에서는 static만사용 하므로static으로 선언한다.
*/
public class LoginManager implements HttpSessionBindingListener{

    private static LoginManager loginManager = null;
    
    //로그인한 접속자를 담기위한 해시테이블
    private static Hashtable loginUsers = new Hashtable();
    
    /*
     * 싱글톤 패턴 사용
     */
    public static synchronized LoginManager getInstance(){
        if(loginManager == null){
            loginManager = new LoginManager();
        }
        return loginManager;
    }
     
    
    /*
     * 이 메소드는 세션이 연결되을때 호출된다.(session.setAttribute("login", this))
     * Hashtable에 세션과 접속자 아이디를 저장한다.
     */
    public void valueBound(HttpSessionBindingEvent event) {
        //session값을 put한다.
        loginUsers.put(event.getSession(), event.getName());
        System.out.println(event.getName() + "님이 로그인 하셨습니다.");
        System.out.println("현재 접속자 수 : " +  getUserCount());
     }
    
    
     /*
      * 이 메소드는 세션이 끊겼을때 호출된다.(invalidate)
      * Hashtable에 저장된 로그인한 정보를 제거해 준다.
      */
     public void valueUnbound(HttpSessionBindingEvent event) {
         //session값을 찾아서 없애준다.
         loginUsers.remove(event.getSession());
         System.out.println("  " + event.getName() + "님이 로그아웃 하셨습니다.");
         System.out.println("현재 접속자 수 : " +  getUserCount());
     }
     
     
     /*
      * 입력받은 아이디를 해시테이블에서 삭제. 
      * @param userID 사용자 아이디
      * @return void
      */ 
     public void removeSession(String userId){
          Enumeration e = loginUsers.keys();
          HttpSession session = null;
          while(e.hasMoreElements()){
               session = (HttpSession)e.nextElement();
               if(loginUsers.get(session).equals(userId)){
                   //세션이 invalidate될때 HttpSessionBindingListener를 
                   //구현하는 클레스의 valueUnbound()함수가 호출된다.
                   session.invalidate();
               }
          }
     }
     
     
     /*
      * 사용자가 입력한 ID, PW가 맞는지 확인하는 메소드
      * @param userID 사용자 아이디
      * @param userPW 사용자 패스워드
      * @return boolean ID/PW가 일치하는 지 여부
      */
     public boolean isValid(String userId, String userPw){
         
         /*
          * 이부분에 Database 로직이 들어간다.
          */
         return true;
     }


    /*
     * 해당 아이디의 동시 사용을 막기위해서 
     * 이미 사용중인 아이디인지를 확인한다.
     * @param userID 사용자 아이디
     * @return boolean 이미 사용 중인 경우 true, 사용중이 아니면 false
     */
    public boolean isUsing(String userID){
        return loginUsers.containsValue(userID);
    }
     
    
    /*
     * 로그인을 완료한 사용자의 아이디를 세션에 저장하는 메소드
     * @param session 세션 객체
     * @param userID 사용자 아이디
     */
    public void setSession(HttpSession session, String userId){
        //이순간에 Session Binding이벤트가 일어나는 시점
        //name값으로 userId, value값으로 자기자신(HttpSessionBindingListener를 구현하는 Object)
        session.setAttribute(userId, this);//login에 자기자신을 집어넣는다.
    }
     
     
    /*
      * 입력받은 세션Object로 아이디를 리턴한다.
      * @param session : 접속한 사용자의 session Object
      * @return String : 접속자 아이디
     */
    public String getUserID(HttpSession session){
        return (String)loginUsers.get(session);
    }
     
     
    /*
     * 현재 접속한 총 사용자 수
     * @return int  현재 접속자 수
     */
    public int getUserCount(){
        return loginUsers.size();
    }
     
     
    /*
     * 현재 접속중인 모든 사용자 아이디를 출력
     * @return void
     */
    public void printloginUsers(){
        Enumeration e = loginUsers.keys();
        HttpSession session = null;
        System.out.println("===========================================");
        int i = 0;
        while(e.hasMoreElements()){
            session = (HttpSession)e.nextElement();
            System.out.println((++i) + ". 접속자 : " +  loginUsers.get(session));
        }
        System.out.println("===========================================");
     }
     
    /*
     * 현재 접속중인 모든 사용자리스트를 리턴
     * @return list
     */
    public Collection getUsers(){
        Collection collection = loginUsers.values();
        return collection;
    }
}

/*

* 본 예제소스는 우리가 구현하려고 예제에서 가장 핵심적인 부분을 맡고있다.
* 여기서 HttpSessionBindingListener는 서블릿 컨테이너에서 세션이 끊길때
* (valueUnBound)와 이를 구현하는 오브젝트가 해당 세션에 setAttribute 될때
* (valueBound)를 호출합니다. 굳이 이를 구현하는이유는 세션이 끊기는 시점을 정확히 잡아내기 위해서다.

* 사용자가 로그아웃버튼을 누를시도 있지만 세션이 타임아웃되는경우도 세션이 끊겨야 하기 때문입니다.


*/



=============================== login.jsp ============================
<%
    /*
     * 로그인 페이지, 로그인전 현재 로그인된 이용자수를 출력한다.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"%>
<%@ page import="test.LoginManager"%>
<%!
    //싱글톤 패턴을 사용하였기 때문에 생생되어있는 인스턴스를 얻어온다.
    LoginManager loginManager = LoginManager.getInstance(); 
%>
<%
    //login_try에서 로그인을 하지 않을경우 세션에 남아있는 userId를 제거한다.
    session.removeAttribute("userId");
%>
<html>
<head>
    <title>로그인 중복방지 Test</title>
</head>
<body>
    <h3 align="center">현재 접속자 수 : <%=loginManager.getUserCount() %>명</h3>
    <form action="login_try.jsp" name="login">
        <div align="center">
            아이디  :   <input type="text" name="userId"><br>
            비밀번호    :   <input type="passward" name="userPw"><br>
            <input type="submit" value="로그인">
        </div>
    </form>
</body>
</html>



============================= login_try.jsp ============================
<%
    /*
     * 로그인 시도페이지, id, pw유무를 체크하고, 올바르다면 
     * 이미 접속한 아이디인지 체크한다. 이미 접속한 아이디라면
     * 기존 접속을 유지할것인지, 기존접속을 kill시키고 로그인할것인지를 
     * 확인한다.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<%@ page import="test.LoginManager"%>
<%!
    //싱글톤 패턴을 사용하였기 때문에 생생되어있는 인스턴스를 얻어온다.
    LoginManager loginManager = LoginManager.getInstance(); 
%>
<html>
<head>
    <title>로그인 중복방지 Test</title>
</head>
<body align="center" valign="center">
<%
    String userId = request.getParameter("userId");
    String userPw = request.getParameter("userPw");
    
    //아이디 패스워드 체크
    if(loginManager.isValid(userId, userPw)){
        
        //접속자 아이디를 세션에 담는다.
        session.setAttribute("userId", userId);
        
        //이미 접속한 아이디인지 체크한다.
        //out.println(userId);
        //out.println(loginManager.isUsing(userId));
        loginManager.printloginUsers();
        if(loginManager.isUsing(userId)){
%>
            이미 접속중입니다. 기존의 접속을 종료하시겠습니까?<P>
            <a href="disconnect.jsp">예 </a>
            <a href="login.jsp">아니오</a>
<%
        }else{
            loginManager.setSession(session, userId);
            response.sendRedirect("login_ok.jsp");
        }
%>
<%
    }else{
%>
        <script>
            alert("로그인후 이용해 주세요.");
            location.href = "login.jsp";
        </script>
<% 
    }
%>
</body>
</html>



========================== disconnect.jsp ============================
<%
    /*
     * login_try.jsp에서 로그인 중복시 무시하고 로그인할경우 호출.
     * 기존의 session을 끊고 hashTable에 저장후 login_ok.jsp를 호출.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<%@ page import="test.LoginManager"%>
<%!
    //싱글톤 패턴을 사용하였기 때문에 생생되어있는 인스턴스를 얻어온다.
    LoginManager loginManager = LoginManager.getInstance(); 
%>
<html>
<head>
    <title>로그인 중복방지 Test</title>
</head>
<body>
<%
    String userId = (String)session.getAttribute("userId");
    if(userId != null){
        //기존의 접속(세션)을 끊는다.
        loginManager.removeSession(userId);
        
        //새로운 세션을 등록한다. setSession함수를 수행하면 valueBound()함수가 호출된다.
        loginManager.setSession(session, userId);
        response.sendRedirect("login_ok.jsp");
    }
%>
</body>
</html>



============================= login_ok.jsp ============================
<%
    /*
     * 정상적으로 로그인되었을경우 호출
     * 접속자 아이디를 보여주고 현재 접속중인 모든 사용자를 뿌려준다.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<%@ page import="java.util.*, test.LoginManager"%>

<%!
    //싱글톤 패턴을 사용하였기 때문에 생생되어있는 인스턴스를 얻어온다.
    LoginManager loginManager = LoginManager.getInstance(); 
%>
<html>
<head>
    <title>로그인 중복방지 Test</title>
</head>
<body align="center" valign="center">
<%
    //jsp내장객체 session을 이용하여 접속자 아이디를 얻어온다.
    String userId = (String)session.getAttribute("userId");

    if(userId != null){
%>
        <%=userId%>님 환영합니다.
        <a href="logout.jsp">로그아웃</a>
        <p>
        현재 접속자 : <br>
<%
        Collection collection = loginManager.getUsers();
        Iterator iterator = collection.iterator();
        int i=0;
        while(iterator.hasNext()){
            out.print((++i)+". "+iterator.next()+"<br>");
        }
    }else{
%>
        <script>
            alert("로그인후 이용해 주세요.");
            location.href = "login.jsp";
        </script>
<% 
    }
%>
</body>
</html>


============================= logout.jsp ==============================
<%
    /*
     * 로그아웃을 클릭했을때 호출된다.
     */
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR" %>
<%
//session을 확~~~끊어 버린다. 이시점에 LoginManager의 valueUnbound()가 호출된다.
session.invalidate();
response.sendRedirect("login.jsp");
%>