본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Spring framework

스프링, 전자정부 프레임워크에서 중복 로그인 확인하기(HttpSessionListener 사용)

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2020. 4. 29.

자바 서블릿 패키지에서 제공하는 HttpSessionListener 인터페이스는 세션의 생성과 소멸에 대해서 이벤트를 감지하도록 되어있다.

아래의 사진을 살펴보면 2개의 생성, 소멸과 관련된 메소드가 존재한다.

메소드 이름이 아주 직관적이다.

 

 

해당 인터페이스를 상속받는 클래스를 만들어주면 세션이 생성 될 때와 소멸될 때의 세션값을 보관해서 소위 말하는 "중복로그인"이 되지 않는 기능을 만들어 볼 수 있다.

먼저 해당 인터페이스를 상속받자.

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionConfig implements HttpSessionListener{
    @Override
    public void sessionCreated(HttpSessionEvent hse) {

    }

    @Override
    public void sessionDestroyed(HttpSessionEvent hse) {

    }
}

 

그리고 세션이 생성될 때의 해당 세션을 기록하기 위한 Map객체를 추가하여주자.

세션이 생성되면 Map객체에 고유 키 값과 session값을 넣어주도록 하자.

마찬가지로 세션이 사라지게 될 경우 Map객체에서 해당 세션을 제거하도록 하여주자.

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionConfig implements HttpSessionListener{

    private static final Map<String, HttpSession> sessions = new ConcurrentHashMap<>();  //세션을 담을 객체

    @Override
    public void sessionCreated(HttpSessionEvent hse) {
        sessions.put(hse.getSession().getId(), hse.getSession());  //getId메소드는 해당 세션의 고유 키값
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent hse) {
        sessions.get(hse.getSession().getId()).invalidate();  
        sessions.remove(hse.getSession().getId());	
    }
}

 

여기까지 하였다면 작업은 거의 끝났다고 볼 수 있다.

세션이 생성될 때는 사용자가 웹 브라우저를 통해서 해당서버로 접속할 때 바로 생성이된다.

그리고 통상적으로 로그인을 하게되는 경우 세션에 로그인을 하는 아이디값이나 기타 정보를 넣어주게된다.

여기서는 사용자가 로그인을 하여 성공한 경우에 userId 라는 값을 세션에 넣었다고 가정하여 보았다.

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionConfig implements HttpSessionListener{

    private static final Map<String, HttpSession> sessions = new ConcurrentHashMap<>();  //세션을 담을 객체

    @Override
    public void sessionCreated(HttpSessionEvent hse) {
        sessions.put(hse.getSession().getId(), hse.getSession());  //getId메소드는 해당 세션의 고유 키값
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent hse) {
        sessions.get(hse.getSession().getId()).invalidate();  
        sessions.remove(hse.getSession().getId());	
    }
    
    //해당 메소드는 로그인 하는 곳에서 호출하는 메소드 이다.
    public synchronized static String loginSessionChecker(String compareId){
        String result = "";
        for( String key : sessions.keySet() ){
            HttpSession value = sessions.get(key);
            if(value != null &&  value.getAttribute("userId") != null && value.getAttribute("userId").toString().equals(compareId) ){
                result =  key.toString();
            }
        }
        removeSessionForDoubleLogin(result);
        return result;
    }
    private static boolean removeSessionForDoubleLogin(String userId){    	
        if(userId != null && userId.length() > 0){
            sessions.get(userId).invalidate();
            sessions.remove(userId);    		
        }
    	return true;
    }    
}

 

loginSessionChecker라는 메소드를 만들어 주었다.

해당 메소드에서의 핵심은 세션 정보가 담긴 Map 객체를 반복문을 통해서 userId라는 값이 혹시나 들어있는지 확인하여 준 다음 만약 존재한다면 세션 고유 아이디를 가져와서 제거하여주는 부분이다.

해당 메소드를 최초 로그인을 시도하는 곳에서 호출한다면 중복된 아이디 값은 제거가 될 것 이다.

 

마지막으로 해당 리스너를 스프링에 등록을 하여야 하는데, 2가지 방법이 존재한다.

web.xml에 listner로 등록하거나 에노테이션으로 할 수 있다.

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener
public class SessionConfig implements HttpSessionListener{

    private static final Map<String, HttpSession> sessions = new ConcurrentHashMap<>();  //세션을 담을 객체

    @Override
    public void sessionCreated(HttpSessionEvent hse) {
        sessions.put(hse.getSession().getId(), hse.getSession());  //getId메소드는 해당 세션의 고유 키값
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent hse) {
        sessions.get(hse.getSession().getId()).invalidate();  
        sessions.remove(hse.getSession().getId());	
    }
    
    //해당 메소드는 로그인 하는 곳에서 호출하는 메소드 이다.
    public synchronized static String loginSessionChecker(String compareId){
        String result = "";
        for( String key : sessions.keySet() ){
            HttpSession value = sessions.get(key);
            if(value != null &&  value.getAttribute("userId") != null && value.getAttribute("userId").toString().equals(compareId) ){
                result =  key.toString();
            }
        }
        removeSessionForDoubleLogin(result);
        return result;
    }
    private static boolean removeSessionForDoubleLogin(String userId){    	
        if(userId != null && userId.length() > 0){
            sessions.get(userId).invalidate();
            sessions.remove(userId);    		
        }
    	return true;
    }    
}

 

마지막으로 해당 클래스를 호출하는 로그인 컨트롤러의 모습을 한번 살펴보자.

개념만 작성한 클래스의 모습이다.


@Controller
public class LoginController {
    
    private LoginService service;

    @RequestMapping(value = "/login")
    public String selectLogin(@RequestParam Map<Object, Object> param, HttpSession session){
    
        //1. 로그인 정보를 확인한다.
        Map<String, String> result = service.getLoginInformation(param); 
        
        if(result != null){ 
            //2. 만약 정보가 일치한 경우 중복로그인을 제거한다.
            SessionConfig.loginSessionChecker(result.get("userId"));
            
            //3. 그리고 새로운 로그인에 따른 해당 아이디값을 세션에 넣어준다.
            session.setAttribute("userId", result.get("userId"));  
            
            //4. 로그인 성공 후 가야할 페이지
            return "redirect:/board";
        }
        
        //5. 로그인 정보가 일치하지 않으면 로그인 페이지로 리다이렉트
        return "redirect:/index"; 
    }
}    

 

크게 어렵지가 않다. 로그인이 성공하면 다시 userId값을 세션에 넣어주어야 하는 것을 잊지 말자.

물론 userId라는 키 값은 원하는데로 바꾸어 사용하면된다..^^

마찬가지로 SessionConfig 클래스에서 ip나 접속 기기여부 같은 조건을 여러개 준 다면 조금 더 다양한 중복로그인 방지 기능을 만들어 볼 수 있다.

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글