[서블릿] 서블릿 쿠키와 세션 2-2

hoonak·2023년 6월 22일
0

서블릿

목록 보기
11/16

세션을 이용한 웹 페이지 연동 기능

쿠키는 사용 시 웹 페이지들의 정보가 클라이언트 PC에 저장되므로 정보가 쉽게 노출될 수 있다는 단점이 있는 반면, 세션은 서버의 메모리에 생성되어 정보를 저장함. 따라서 웹 페이지에서 사용되는 정보 중에 로그인 정보처럼 보안이 요구되는 정보는 대부분 세션을 이용함.

세션은 각 브라우저당 한 개, 즉 사용자당 한 개가 생성됨. 사용자의 로그인 상태나 쇼핑몰의 장바구니 담기 기능 같은 정보를 해당 브라우저의 세션에 저장해 두고 사용하면 편리함.

세션의 특징

  • 정보가 서버의 메모리에 저장됨.
  • 브라우저의 세견 연동은 세션 쿠키를 이용함.
  • 쿠키보다 보안에 유리함.
  • 서버에 부하를 줄 수 있음.
  • 브라우저(사용자)당 한 개의 세션(세션id)이 생성됨.
  • 세션은 유효 시간을 가짐(기본 유효 시간은 30분 임).
  • 로그인 상태 유지 기능이나 쇼핑몰의 장바구니 담기 기능 등에 주로 사용됨.

세션 기능 실행 과정
1. 브라우저로 사이트에 접속함.
2. 서버는 접속한 브라우저에 대한 세션 객체를 생성함.
3. 서버는 생성된 세션 id를 클라이언트 브라우저에 응답함.
4. 브라우저는 서버로부터 받은 세션 id를 브라우저가 사용하는 메모리의 세션 쿠키에 저장함(쿠키 이름은 jsessionID).
5. 브라우저가 재접속하면 브라우저는 세션 쿠키에 저장된 세션 id를 서버에 전달함.
6. 서버는 전송된 세션 id를 이용해 해당 세션에 접근하여 작업을 수행함.

세션의 중요한 특징은 브라우저당 한 개씩 생성된다는 점. 그러므로 브라우저가 서버에 접속하여 브라우저에 저장된 세션 id(jsessionID)를 전송하면 서버는 그 값을 이용해서 해당 브라우저에 대한 세션을 구분하고 각 브라우저에 대한 세션 작업을 수행함.

세션 API의 특징과 기능
서블릿에서 세션을 이용하려면 HttpSession 클래스 객체를 생성해서 사용해야 함. HttpSession 객체는 HttpServletRequest의 getSession() 메서드를 호출해서 생성함.

세션을 얻는 getSession() 메서드로는 다음과 같은 것들이 있음.

  • getSession() : 기존의 세션 객체가 존재하면 반환하고, 없으면 새로 생성함.

  • getSession(true) : 기존의 세션 객체가 존재하면 반환하고, 없으면 새로 생성함.

  • getSession(false) : 기존의 세션 객체가 존재하면 반환하고, 없으면 null을 반환함.

HttpSession 클래스의 여러 가지 메서드

  • getAttribure(String name) : 속성 이름이 name인 속성 값을 Object 타입으로 반화함. 해당되는 속성 이름이 없을 경우 null 값을 반환함.

  • getAttributeNames() : 세션 속성 이름들을 Enumeration 객체 타입으로 반환함.

  • getCreationTime() : 1970년 1월 1일 0시 0초를 기준으로 현재 세션이 생성된 시간까지 경과한 시간을 계산하여 1/1000초 값으로 반환함.

  • getId() : 세션에 할당된 고유 식별자를 String 타입으로 변환함.

  • getMaxInactiveInterval() : 현재 생성된 세션을 유지하기 위해 설정된 세션 유지 시간을 int 타입으로 반환함.

  • inavalidate() : 현재 생성된 세션을 소멸함.

  • isNew() : 최초로 생성된 세션인지 기존에 생성되어 있었던 세션인지를 판별함.

  • removeAttribute(String name) : 세션 속성 이름이 name인 속성을 제거함.

  • setAttribute(String name, Object value) 세션 속성 이름이 name인 속성에 속성 값으로 value를 할당함.

  • setMaxInactiveInterval(int interval) : 세션을 유지하기 위한 세션 유지 시간을 초 단위로 설정함.

예 1) 서블릿에서 세션 API 이용하기

request의 인자 없는 getSession() 메서드를 호출하여 세션이 없으면 새로 생성하고, 세션이 있으면 기존 세션을 가져옴. 또한 세션 객체의 getMaxInactiveInterval()를 호출하여 생성된 세션의 유효 시간을 가져옴.

SessionTest.java

package sec03.ex01;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/sess")
public class SessionTest extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html; charset = utf-8");
		PrintWriter out = response.getWriter();
		
		// getSession()을 호출하여 최초 요청 시 세션 객체를 새로 생성하거나 기존 세션을 반환함.
		HttpSession session = request.getSession();
		
		 // 생성된 세션 객체의 id를 가져옴.
		out.println("세션 아이디 : " + session.getId() + "<br>");
		
		// 최초 세션 객체 생성 시간을 가져옴.
		out.println("최초 세션 생성 시각 : " + new Date(session.getCreationTime()) + "<br>"); 
		
		// 세션 객체에 가장 최근에 접근한 시간을 가져옴.
		out.println("최근 세션 접근 시각 : " + new Date(session.getLastAccessedTime()) + "<br>"); 
		
		// 세션 객체의 유효 시간을 가져옴.
		out.println("세션 유효 시간 : " + new Date(session.getMaxInactiveInterval()) + "<br>"); 
		
		if(session.isNew()) { // 최초 생성된 세션인지 판별함.
			out.println("새 세션이 만들어졌습니다.");
		}	
	}
} //세션 유효 시간을 따로 설정하지 않으면 톰캣에서 설정한 기본 유효 시간 30분이 적용 됨.

브라우저에서 최초 요청 시 생성된 세션 객체에 할당된 세션 id와 여러 가지 정보를 출력함. 최초 생성된 새션이므로 "새 세션이 만들어졌습니다" 라는 메시지가 출력 됨.

같은 브라우저에서 다른 탭을 열고 요청하면 같은 세션 id를 출력하므로 최초 생성된 세션을 재사용 함. 따라서 "새 세션이 만들어졌습니다"라는 메시지는 출력되지 않음.

세션 기본 유효 시간은 톰캣 컨테이너에서 지정한 30분임. 그러나 HttpSession의 setMaxInactiveInterval() 메서드를 이용하면 사용자가 원하는 세션 유효 시간을 서정할 수 있음. 그리고 inavaldate() 메서드를 이용하면 세션을 언제든지 삭제할 수도 있음.


이미지) Servers에 web.xml

예 2) 제공되는 메서드들을 이용해서 직접 세션 유효 시간을 재설정

setMaxInactiveIntervla() 메서드를 이용해 세션 유효 시간을 5초로 재설정.

SessionTest2.java

package sec03.ex02;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/sess2")
public class SessionTest2 extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html; charset=utf8");
		PrintWriter out = response.getWriter();
		
		HttpSession session = request.getSession();
		out.println("세션 아이디 : " + session.getId() + "<br>");
		out.println("최초 세션 생성 시각 : " + new Date(session.getCreationTime()) + "<br>");
		
		// 톰캣의 기본 세션 유효 시간을 출력함.
		out.println("기본 세션 유효 시간 : " + session.getMaxInactiveInterval() + "<br>");
		
		// 세션의 유효 시간을 5초로 설정함.
		session.setMaxInactiveInterval(5);
		
		// 유효 시간을 재설정한 후 세션 유효 시간을 출력함.
		out.println("세션 유효 시간 : " + session.getMaxInactiveInterval() + "<br>");
		
		if(session.isNew()) {
			out.print("새 세션이 만들어 졌습니다");
		}
	}

}

5초가 지난 후 같은 브라우저에서 재요청하면 다시 새 세션이 생성됨. 즉 5초 이후에는 기존 세션은 삭제되고 재요청 시 새 세션이 생성됨.

세션 유효 시간을 재설정하는 경우는 주로 은행 사이트에서 자주 발생함. 우리가 보통 은행 사이트에 로그인하면 화면 위쪽에 10분에서 초 단위로 역카운팅이 되는 것이 보임. 만약 10분 이상 아무 작업도 하지 않으면 로그인 상태를 기억하는 세션이 자동 삭제되어 자동 로그아웃 화면을 출력함.

예 3) invalidate() 메ㅔ서드를 이용해 강제로 세션을 삭제

inalidate() 메서드를 이용해 강ㅈ로 세션을 삭제함.

SessionTest3.java

package sec03.ex02;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/sess3")
public class SessionTest3 extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html; charset = utf-8");
		PrintWriter out = response.getWriter();
		
		HttpSession session = request.getSession();
		out.println("세션 아이디 : " + session.getId() + "<br>");
		out.println("최초 세션 생성 시각 : " + new Date(session.getCreationTime()) + "<br>");
		out.println("최초 세션 접근 시각 : " + new Date(session.getLastAccessedTime()) + "<br>");
		out.println("세션 유효 시간 : " + session.getMaxInactiveInterval() + "<br>");
		
		if(session.isNew()) {
			out.print("새 세션이 만들어졌습니다"); 
		}
		session.invalidate(); //// invalidate()를 호출하여 생성된 객체를 강제로 삭제함.
	}


}

최초 요청 시 새 세션이 생성된 후 invalidate() 메서드가 호출되므로 바로 소멸됨.

예 4) 세션을 이용한 로그인 정보 바인딩

지금까지는 HttpServletContext와 HttpServletRequest의 바인딩 기능을 사용했음. 그런데 로그인 상태처럼 사용자와 관련된 정보를 바인딩해서 사용할 때는 이용하는 것이 편리함. 세션은 사용자당 한 개씩 생성되기 때문. 이번 절에서는 로그인을 이용해 HttpSession의 바인딩 기능을 알아보겠음.

실습하기 전에 해야 할 일이 있음. 톰캣이 종료된 후에도 세션이 메모리에서 삭제되지 않는 경우가 있으므로 톰캣 설정 팔이인 context.xml을 열어 Manager pathname = "" 태그의 주석을 해제해야 함.

로그인 상태처럼 사용자와 관련된 정보를 바인딩해서 사용할 때는 세션을 이용하는 것이 편리함. 세션은 사용자당 한 개씩 생성되기 때문.

login.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form name = "frmLogin" method = "post" action = "login" encType = "UTF-8">
		아이디 : <input type = "text" name = "user_id"><br>
		비밀번호 : <input type = "password" name = "user_pw"><br>
		<input type = "submit" value = "로그인">
		<input type = "reset" value = "다시 입력">
	</form>
</body>
</html>

로그인창에서 로그인한 경우 id와 비밀번호를 가져오고, 최초 요청 시 세션에 setAttribute() 메서드를 이용해 user_id로 사용자 id를 바인딩하도록 구현함. a 태그를 이용해 재요청하고 세션의 getSttribute()메서드를 이용하여 user_id 값을 가져와 로그인 여부를 확인함.

SessionTest4.java

package sec03.ex02;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

//@WebServlet("/login")
public class SessionTest4 extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}
	
	private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html; charset=utf-8");
		PrintWriter out = response.getWriter();
		
		HttpSession session = request.getSession();
		 //로그인창에서 전송된 ID와 비밀번홀르 가져 옴.
		String user_id = request.getParameter("user_id");
		String user_pw = request.getParameter("user_pw");
		
		if(session.isNew()) { // 최초 요청 시 수행함.
			if(user_id != null) { // 로그인창에서 서블릿 으로 요청했다면 ID가 null이 아니므로 세션에 ID를 바인딩 함.
				session.setAttribute("user_id", user_id);
				out.println("<a href = 'login'> 로그인 상태 확인 </a>");
			}else {
				out.print("<a href = 'login2.html'> 다시 로그인 하세요 </a>");			
				session.invalidate();
			}			
		}else { // 재요청시 세션에서 ID를 가져와 이전에 로그인 했는지 여부를 확인 함.
			user_id = (String)session.getAttribute("user_id");
			if(user_id != null && user_id.length() != 0) {
				out.print("안녕하세요 " + user_id + "님.");
			}else {
				out.print("<a href = 'login2.html'> 다시 로그인 하세요 </a>");
				session.invalidate();
			}
		}	
	}
}

encodeURL() - 사용자가 쿠키 금지 했을 경우 해결 방법

세션 역시 클라이언트의 세션 쿠키를 이용해 각 브라우저에 대한 세션 기능을 사용함. 그런데 만약 브라우저에서 쿠키 기능을 사용할 수 없게 설정했다면 쿠키 기능은 물론 세션 기능도 사용할 수 없음. 이럴 때는 encodeURL() 메서드를 이용해 직접 서버에서 브라우저로 응답을 먼저 보낸 후 URL Rewriting 방법을 이용해 jsessionId를 서버로 전송하여 세션 기능을 사용하면 됨.

예 ) encodeURL() 메서드를 이용한 세션 사용

package sec04.ex01;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/login")
public class SessionTest5 extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doHandle(request, response);
	}
	
	private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html; charset=utf-8");
		PrintWriter out = response.getWriter();
		
		HttpSession session = request.getSession();
		String user_id = request.getParameter("user_id");
		String user_pw = request.getParameter("user_pw");
		
		if(session.isNew()) {
			if(user_id != null) {
				session.setAttribute("user_id", user_id);
				// 변수 url에 encodeURL()을 이용해 응답 시 미리 jsessionId를 저장함.
				String url = response.encodeURL("login");
				// 로그인 상태 확인 클릭 시 jessionId를 서블릿으로 다시 전송함.
				out.println("<a href = " + url + ">로그 상태 확인 </a>");
			}else {
				out.print("<a href = 'login2.html>다시 로그인 하세요</a>");
				session.invalidate();
			}
		}else {
			user_id = (String) session.getAttribute("user_id");
			if(user_id != null && user_id.length() != 0) {
				out.print("안녕하세요 " + user_id + "님");
			}else {
				out.print("<a href = 'login2.html'> 다시 로그인하세요</a>");
				session.invalidate();
			}
		}
		
	}

}
profile
Hello World!

0개의 댓글