9장. 쿠키와 세션 알아보기
9.1 웹 페이지 연결 기능
데이터를 DB에서 가져올 수도 있지만 어떤 정보들은 클라이언트pc나 서버의 메모리에 저장해두고 사용하면 속도향상 할 수 있음.
* HTTP 프로토콜이 각 웹 페이지의 상태나 정보를 다른 페이지들과 공유하지 않는 stateless 방식으로 통신함
>> 웹 페이지 사이의 상태나 정보를 공유하려면 프로그래머가 세션 트래킹이라는 웹 페이지 연결 기능을 구현해야 함
<웹 페이지를 연동하는 방법>
- <hidden> 태그
- URL Rewriting : GET 방식으로 URL 뒤에 정보를 붙여서 다른 페이지로 전송
- 쿠키 : 클라이언트 PC의 Cookie 파일에 정보를 저장 후 공유
- 세션 : 서버 메모리에 정보를 저장한 후 공유
9.2 <hidden> 태그와 URL Rewriting 이용해 웹 페이지 연동하기
- <hidden> 태그 ex)
<form ...
<input type="hidden" name="user_hp" value="010-111-2222">
</form>
- URL Rewriting ex)
- first
...
user_address=URLEncoder.encode(user_address,"utf-8"); //한글을 전송하기 위한 인코딩
out.print("<br><a href='/pro09/second?user_id="+user_id+"&user_pw="+user_pw+"&user_address="+user_address+"'>두번 째 서블릿으로 보내기</a>");
-second
...
String user_id=request.getParameter("user_id"); // ?로 구분되어 보낸것이 name-value가 됨
>>> <hidden>, URLRewritng 방식은 웹 페이지가 많아지면 일일이 정보를 다른 웹페이지로 전송해야하고 특히 URLRewriting은 보안상으로도 좋지 않고 전송할 수 있는 데이터 용량에도 한계가 있다는 단점이 있음. >> 간단한 정보 공유할때만 사용
9.3 쿠키를 이용한 웹 페이지 연동 기능
- 쿠키 : 웹 페이지들 사이의 공유 정보를 클라이언트 PC에 저장해 놓고 필요할 대 여러 웹 페이지들이 공유해서 사용할 수 있도록 매개 역할을 하는 방법
쿠키 특징
- 저장 정보 용량에 제한이 있음(파일 용량: 4KB)
- 보안 취약
- 클라이언트 브라우저에서 사용 유무를 설정할 수 있음
- 도메인당 쿠키가 만들어짐(1웹사이트 1쿠키)
쿠키의 종류 (Persistence 쿠키, Session 쿠키)
속성 | Persistence 쿠키 | Session 쿠키 |
생성 위치 | 파일로 생성 | 브라우저 메모리에 생성 |
종료 시기 | 쿠키를 삭제하거나 쿠키 설정 값이 종료된 경우 | 브라우저를 종료한 경우 |
최초 접속시 전송 여부 | 최초 접속시 서버로 전송 | 최초 접속시 서버로 전송되지 않음 |
용도 | 로그인 유무 또는 팝업창 제한할 때 | 사이트 접속시 Session 인증 정보 유지할 때 |
클라이언트 PC에서 쿠키 파일이 생성되는 위치
- 크롬 : C:\U
- 인터넷 익스플로어 : 도구>인터넷 옵션>설정>파일보기
쿠키 기능 실행 과정
1. 브라우저로 사이트 접속
2. 서버 : 정보를 저장한 쿠키 생성 후 브라우저로 전송
3. 브라우저 : 서버로부터 받은 쿠키 정보를 쿠키 파일에 저장
4. : 브라우저가 다시 접속해 서버가 브라우저에게 쿠키 전송을 요청할 때 브라우저는 쿠키 정보를 서버에 넘겨줌
5. 서버 : 쿠키 정보를 이용해 작업
쿠키 API
- Cookie 클래스 객체를 생성하여 정보를 저장한 후 서버에서 클라이언트로 전송해 파일로 저장됨
- HttpServletResponse의 addCookie() : 서버 -> 브라우저, 브라우저는 이를 저장
- HttpServletRequest의 getCookie() : 브라우저 -> 서버
<Cookie 클래스의 여러가지 메서드>
getComment(), setComment(String) | 쿠키에 대한 설명을 가져오고 설정 |
getDomain(), setDomain(String) | 쿠키의 유효한 도메인 정보 |
getMaxAge(), setMaxAge(int) | - 쿠키 유효 기간(단위 :초) - 인자값이 음수이면 Session 쿠키생성 (디폴트) 인자값이 양수이면 Persistence 쿠키 생성 |
getName() | 쿠키 이름 |
getPath(), setPath() | 쿠키의 디렉토리 정보 |
getValue(), setValue(String) | 쿠키의 설정 값 |
ex)
set.java
Cookie c=new Cookie("cookieTest",URLEncoder.encode("JSP프로그래밍입니다.","utf-8")); //cookieTest라는 이름으로 내용저장
c.setMaxAge(24*60*60);
response.addCookie(c);
get.java
...
Cookie[] allValues=request.getCookies();
for(int i=0;i<allValues.length;i++) {
if(allValues[i].getName().equals("cookieTest"))
out.print("<h2>Cookie 값 가져오기 : "+URLDecoder.decode(allValues[i].getValue(),"utf-8"));
}
쿠키 생성 상태 확인하기
F12를 누르고
세션 쿠키 사용하기
>> 위의 코드에서 c.setMaxAge(-1)처럼 음수로 지정
>> 쿠키를 브라우저 메모리에 저장, 결과는 persistence와 같음
쿠키 이용해 팝업창 제한하기
>> 서버가 아닌 자바스크립트를 이용해 쿠키에 직접 접근
팝업창.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>팝업창</title>
<script type="text/javascript">
function setPopUpStart(obj){
if(obj.checked==true){
var expireDate =new Date();
var days=1;
expireDate.setDate(expireDate.getDate()+days); <!-- 팝업쿠키 유효 시간을 하루로 설정 -->
document.cookie="notShowPop="+"true"+";path=/; expires="+expireDate.toGMTString();
window.close(); <!--팝업창 닫기 -->
}
}
</script>
</head>
<body>
알림 팝업창입니다.
<br> <br> <br> <br> <br> <br> <br>
<form>
<input type=checkbox onClick="setPopUpStart(this)">오늘 더 이상 팝업창 띄우지 않기
</form>
</body>
</html>
뒷화면.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>자바스크립트에서 쿠키 사용</title>
<script type="text/javascript">
window.onload=pageLoad; <!-- 브라우저에 페이지가 로드될 때 pageLoad()함수 호출 -->
function pageLoad(){
notShowPop =getCookieValue();
if(notShowPop!="true")
window.open("popUp.html","pop","width=400,height=500,history=no,resizable=no,status=no,scrollbars=yes,menubar=no");
}
function getCookieValue(){
var result="false";
if(document.cookie!=""){
cookie=document.cookie.split(";"); <!-- document의 cookie 속성으로 쿠키 정보를 문자열로 가지고 옴-->
<!-- 가져온 정보를 ;로 분리해 각각의 쿠키를 얻음 -->
for(var i=0;i<cookie.length;i++){
element=cookie[i].split("=");
value=element[0];
value=value.replace(/^\s*/,''); <!-- 정규식을 이용해 쿠키 이름 문자열의 공백(\s)제거 -->
if (value=="notShowPop") <!-- 여러 쿠키 중 notShowPop이름의 쿠키면 값을 가져옴-->
result=element[1];
}
}
return result
}
function deleteCookie(){
document.cookie="notShowPop="+"false"+";path=/; expires=-1"; <!-- 쿠키 삭제 클릭 시 notShowPop 쿠키의 값을 false로 지정 -->
}
</script>
</head>
<body>
<form>
<input type=button value="쿠키삭제" onClick="deleteCookie()">
</form>
</body>
</html>
9.4 세션을 이용한 웹 페이지 연동 기능
<세션의 특징>
- 정보가 서버의 메모리에 저장됨
- 브라우저의 세션 연동은 세션 쿠키를 이용
- 쿠키보다 보안에 유리
- 서버에 부하를 줄 수 있음
- 브라우저당 한 개의 세션(세션id)가 생성
- 세션은 유효 시간을 가짐(default : 30분)
- 사용ex) 로그인 상태 유지 기능, 쇼핑몰의 장바구니 기능
<세션 기능 실행 과정>
1. 브라우저로 사이트에 접속
2. 서버: 접속한 브라우저에 대한 세션 객체를 생성
3. : 생성된 세션 id를 클라이언트 브라우저에 응답
4. 브라우저 : 서버로부터 받은 세션 id를 브라우저가 사용하는 메모리의 세션 쿠키에 저장함(쿠키 이름은 jessionId)
5. : 브라우저가 재접속하면 세션 쿠키에 저장된 세션 id를 서버에 전달
6. 서버 : 전송된 세션 id를 이용해 해당 세션에 접근하여 작업 수행
<세션 API의 특징과 기능>
- HttpSession 클래스 객체를 생성해서 사용.
- HttpServletRequest.geSession() 호출해서 객체 생성
<세션을 얻는 getSession() 메서드>
세개 모두 기존의 세션 객체가 존재하면 반환
- getSession(), getSession(true) : 없으면 새로 생성
- getSession(false) : 없으면 null 반환
<HttpSession 클래스의 여러가지 메서드>
반환 타입 | 메서드 | 설명 |
Object | getAttribute(String name) | 속성 이름이 name인 속성값을 Object 타입으로 반환, 없으면 null 반환 |
Enumeration | getAttributeNames() | 세션 속성 이름들을 Enumeration 객체 타입으로 반환 |
long | getCreationTime() | 1970년 1월 1일 0시 0초를 기준으로 현재 세션이 생성된 시간까지 경과한 시간을 계산하여 1/1000초 값으로 반환 |
String | getId() | 세션에 할당된 고유 식별자를 String 타입으로 반환 |
int | getMaxInactiveInterval() | 세션 유지 시간을 int 타입으로 반환(단위 :초) |
void | invalidate() | 현재 생성된 세션 소멸 |
boolean | isNew() | 최초로 생성된 세션인지 기존에 생성되어 있었던 세션인지 판별 |
void | removeAttribute(String name, Object value) | 세션 속성 이름이 name인 속성 제거 |
void | setAttribute(String name, Object value) | 세션 속성 이름이 name인 value 반환 |
void | setMaxInactiveInterval(int interval) | 세션을 유지하기 위한 유지 시간 초단위로 설정 |
~서블릿에서 세션API 이용하기~
...
HttpSession session=request.getSession();
String data="세션 아이디 : "+session.getId();
data+="<br>최초 세션 생성 시각 : "+new Date(session.getCreationTime());
data+="<br>최근 세션 접근 시각 : "+new Date(session.getLastAccessedTime());
data+="<br>세션 유효 시간 : "+session.getMaxInactiveInterval();
out.print(data);
if(session.isNew()) {
out.print("<br>새 세션이 만들어졌습니다.");
}
>> 브라우저마다 다른 아이디가 출력, 새 브라우저에 최초로 접속할 때만 '새 세션이 만들어졌습니다' 출력됨
* 세션 기본 유효 시간 : 톰캣 컨테이너(Servers)의 web.xml 에서 확인 가능
* setMaxInactiveInterval(n)로 세션 기본 유효시간 설정 가능
=> 최근 세션 접근 시각+n초마다 새 세션 id로 갱신됨
=> 주로 은행 사이트에서 자주 사용(n분동안 아무 작업도 하지 않으면 로그인 상태를 기억하는 세션이 자동 삭제되어 자동 로그아웃 됨)
세션을 이용한 로그인 정보 바인딩 실습
- 지금까지는 HttpServletContext와 HttpServletRequest의 바인딩 기능을 사용함
- 로그인 상태처럼 사용자와 관련된 정보를 바인딩해서 사용할 때는 세션을 이용하는 것이 편리
톰캣이 종료된 후에 세션이 메모리에서 삭제되지 않는 경우 방지
>> 톰캣 설정 파일의 context.xml <Manager pathname="" /> 태그의 주석을 해제
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);
out.print("<a href='login2'>로그인 상태 확인</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();
}
}
}
9.5 encodeURL() 사용법
* 세션도 클라이언트의 세션쿠키를 이용한다.
* but 브라우저에서 쿠키 기능을 사용할 수 없게 설정했다면??
>> encodeURL()로 직접 서버에서 브라우저로 응답을 보내고 URLRewriting 방법을 이용해서 jessionId를 서버로 전송
* 쿠키가 허용 되었을 때는 처음 요청 응답 때 서버는 jsessionId를 전해주고 클라에 저장하지만
쿠키가 금지 됐을 때는 저장할 공간(쿠키)가 없기 때문에 처음 응답때 응답만하고 두번째 요청 때 jsessionId를 명시한 url로 연결하여 jsessionId를 서블릿으로 전송함
* 크롬에서 쿠키 사용금지하기 : 설정>개인정보 및 보안> 쿠키 및 기타 사이트 데이터 > 쿠키차단
* response.encodeUrl("login2") 로 변수에 미리 jessionId를 저장해놓기
session.setAttribute("user_id", user_id);
String url=response.encodeUrl("login2"); //변수 url에 미리 jessionId를 저장해놓음
out.print("<a href="+url+">로그인 상태 확인</a>");
9.6 세션을 이용한 로그인 예제
db와 연동한 후 세션을 이용해 로그인 상태를 유지하기
=> 세션에 로그인 상태 저장해서 각 웹페이지를 열 때마다 세션에서 로그인 상태를 확인한다.
- loginServlet
String user_id=request.getParameter("user_id");
String user_pw=request.getParameter("user_pw");
MemberVO memberVO=new MemberVO();
memberVO.setId(user_id);
memberVO.setPwd(user_pw);
MemberDAO dao=new MemberDAO();
if(dao.isExisted(memberVO)) {
HttpSession session=request.getSession();
session.setAttribute("isLogon", true);
session.setAttribute("login.id", user_id);
session.setAttribute("login.pwd", user_pw);
out.print("안녕하세요 "+user_id+"님!! <br>");
out.print("<a href='show'>내정보 보기</a>");
}else {
out.print("아이디 또는 비밀번호가 틀립니다.<br>");
out.print("<a href='login3.html'>다시 로그인하기</a>");
}
- DAO
private DataSource dataFactory;
private PreparedStatement pstmt;
private Connection con;
public MemberDAO() {
try {
Context ctx=new InitialContext();
Context envContext=(Context)ctx.lookup("java:/comp/env");
dataFactory=(DataSource)envContext.lookup("jdbc/oracle");
}catch(Exception e) {
e.printStackTrace();
}
}
public boolean isExisted(MemberVO memberVO) {
boolean result=false;
try {
con=dataFactory.getConnection();
String query="select decode(count(*),1,'true','false') as result from t_member where id=? and pwd=?";
//오라클의 decode함수로 조건에 맞는 레코드가 있으면 true, 없으면 false반환
pstmt=con.prepareStatement(query);
pstmt.setString(1, memberVO.getId());
pstmt.setString(2, memberVO.getPwd());
ResultSet rs=pstmt.executeQuery();
rs.next(); //커서를 첫번째 레코드로
result=Boolean.parseBoolean(rs.getString("result")); //result필드 값 얻기
}catch(Exception e) {
e.printStackTrace();
}
return result;
}
- show
HttpSession session=request.getSession(false);
if(session!=null) {
if((Boolean)session.getAttribute("isLogon")) {
String user_id=(String)session.getAttribute("login.id");
String user_pw=(String)session.getAttribute("login.pwd");
out.print("아이디 : "+user_id+"<br>비밀번호 : "+user_pw);
}
else
response.sendRedirect("login3.html");
}else
response.sendRedirect("login3.html");
}