본문 바로가기

JSP/SessionListener Annotation

SessionListener with Annotation

HttpSessionListener 작성시 @WebListener Annotation 을 사용하는 예


HttpSessionListener 를 작성하면서 web.xml 파일에 해당 클래스를 등록해야만 작동했지만 Annotation이 지원되면서 이제는 web.xml 파일에 등록하지 않아도 된다. 오히려 web.xml 파일이 존재할 때 Annotation을 사용하면 설정내용이 제대로 작동하지 않는 것을 확인할 수 있었다.


아래의 예제에서 사용된 Base64 인코딩/디코딩 기능은 HttpSession 아이디를 URL에 포함하여 전송할 때 사용할 수 있지만 여기서는 반드시 필요한 것은 아니며 HttpSession 아이디를 AES 알고리듬 등으로 암호화하여 전송하는 경우에는 암호화 결과 데이터 중에는 URL에는 사용할 수 없는 특수문자도 포함되므로 아래처럼 아파치 Base64 라이브러리를 사용하여 Base64 인코딩 문자열을 사용한다면 문제를 해결할 수 있다

commons-codec-1.9.jar



package org.kdea.java.sessionlistener;

import java.util.*;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.*;

@WebListener()
public class SessionHandler implements HttpSessionListener {

	private static Map<String,HttpSession> sessions = new HashMap<>();
	
	@Override
	public void sessionCreated(HttpSessionEvent evt) {
		HttpSession s = evt.getSession();
		String sid = s.getId();
		sessions.put(sid, s);
		System.out.println("세션 생성 "+s.getId());
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent evt) {
		HttpSession s = evt.getSession();
		String sid = s.getId();
		System.out.println("세션 삭제 "+s.getId());
		sessions.remove(sid);
	}
	
	public static Map<String,HttpSession> getMap() {
		return sessions;
	}

}



위에서 정의한 SessionHandler 클래스를 JSP에서 사용하는 예

email_confirm_link.jsp

<%@page import="org.apache.commons.codec.binary.Base64"%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<%
	/*
	이용자의 메일주소를 확인할 때,
	이용자가 입력한 메일주소로 확인메일을 보내고 이용자는 그 메일을 수신하여 메일내용 중에
	있는 링크를 클릭하면 서버로 요청이 전달되어 그 메일주소의 유효성이 검증되는 방법을 주로 사용한다.
	이 때 메일을 수신한 이용자는 메일 서비스를 제공하는 웹사이트로 이동하게 되므로 현재 사이트 외부에서 
	다시 접속하는 상황이 된다.
	이런 경우에는 현재 사이트에서 새로운 세션이 생성되는 것을 확인할 수 있었다. 
	인터넷 자료를 보면 브라우저에 따라서 혹은 브라우저 사용환경에 따라서 한 이용자에게 다수개의 세션이
	생성될 수도 있다고 했는데, 이 경우라면 서버에 생성된 기존 세션 객체에 저장된 이용자의 데이터는
	잃게 되므로 심각한 문제를 야기할 수 있다. 
	이 예제는 한 이용자에게 기존 세션객체 외에 새로운 세션객체가 생성되더라도 
	새로운 세션객체를 무시하고 기존 세션객체를 계속 사용할 수 있도록 하는 내용이다.
	이용자에게 메일을 보낼 때 기존 세션 아이디를 링크의 파라미터에 포함하여 전달하고 
	이용자가 메일에서 그 링크를 클릭하면 요청을 처리하는 페이지에서 이용자의 세션 아이디를 추출하여
	기존 세션을 찾아서 현재의 세션객체로 사용하는 예이다.
	URL Rewriting 방법이 통하지 않는 경우에도 이 방법은 문제 없이 작동되며, 세션 아이디를
	암호화하여 전달한다면 보안상 문제도 해결될 수 있을 것이다.
	여기서는 URL에 세션 아이디가 포함될 때 Base64문자열로 인코딩하여 전송하는 방법을 사용했는데,
	세션 아이디를 암호화하는 경우에는 URL에 포함할 수 없는 특수문자도 있기 때문에 아래처럼 Base64문자열로
	인코딩하여 URL에 포함하면 문제가 없을 것이다
	*/
	session.setAttribute("userData", "This is user data");

	String sid = session.getId();
	
	Base64 base64 = new Base64();
	byte[] tmp = base64.encode(sid.getBytes());
	String base64Str = new String(tmp);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title></title>
</head>
<body>

<%-- 이용자에게 확인 메일을 전송할 때 아래와 같은 링크가 포함된 메일 내용을 작성한다 --%>
<a href="activate.jsp?s=<%=base64Str%>">Confirm My Email</a>

</body>
</html>



위의 페이지에서 링크를 클릭하면 실행되는 JSP

activate.jsp

<%@page import="org.apache.commons.codec.binary.Base64"%>
<%@page import="org.kdea.java.sessionlistener.SessionHandler"%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<%
	String sessionId = request.getParameter("s");

	Base64 base64 = new Base64();
	byte[] tmp = base64.decode(sessionId.getBytes());
	String sid = new String(tmp);
	/*이 페이지 접속시 세션이 새로 생성되는 문제점이 있다고 가정하고...
	아래처럼 현재 세션 참조변수에 기존 세션객체의 참조를 전달하여 새로 생성된 세션객체를 무시하고
	기존 세션객체에 저장된 이용자 데이터를 계속 사용할 수 있다
	*/
	session = SessionHandler.getMap().get(sid);
	String userData = (String)session.getAttribute("userData");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title></title>
</head>
<body>
유효한 메일주소입니다
<p>
<%=userData%>
</body>
</html>