HttpSessionListener 작성시 @WebListener Annotation 을 사용하는 예
HttpSessionListener 를 작성하면서 web.xml 파일에 해당 클래스를 등록해야만 작동했지만 Annotation이 지원되면서 이제는 web.xml 파일에 등록하지 않아도 된다. 오히려 web.xml 파일이 존재할 때 Annotation을 사용하면 설정내용이 제대로 작동하지 않는 것을 확인할 수 있었다.
아래의 예제에서 사용된 Base64 인코딩/디코딩 기능은 HttpSession 아이디를 URL에 포함하여 전송할 때 사용할 수 있지만 여기서는 반드시 필요한 것은 아니며 HttpSession 아이디를 AES 알고리듬 등으로 암호화하여 전송하는 경우에는 암호화 결과 데이터 중에는 URL에는 사용할 수 없는 특수문자도 포함되므로 아래처럼 아파치 Base64 라이브러리를 사용하여 Base64 인코딩 문자열을 사용한다면 문제를 해결할 수 있다
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>