Staless 프로토콜
- 세션에 대해 알아보기전에 우리는 HTTP의 프로토콜 특징에 대해 알아보고자 합니다.
- HTTP 프로토콜을 기본적으로 클라이언트의 요청(request), 응답(response)로 구현되어 있습니다.
- 이 말은, 서버에 요청을 하고 응답을 받으면 서버와의 통신이 끊기게 되고, 서버에는 클라이언트의 어떠한 정보도 유지하지 않는다는 뜻입니다.
- 이것을 HTTP의 Staless특성인 즉, 비연결형 프로토콜이라고 합니다.
- 장점
- 프로토콜을 직관적으로 이해하기 쉽고, 구현이 단순합니다.
- 클라이언트의 요청에 대한 처리 결과를 응답으로 전송하고 나면 접속이 종료되기 때문에, 서버측 네트워크 자원의 효율 성이 증가합니다. 즉, 더 많은 클라이언트 요청을 처리 할 수 있습니다.
- 단점
- 각 클라이언트 요청마다 새로운 접속이 이루어지기 때문에 서버측의 네트워크 자원의 낭비가 적지만, 새로운 접속을 맺기 위해 발생하는 오버헤드 즉, 수행속도의 감소가 지속적으로 발생할 수 있습니다.
- 또한, 동일한 클라이언트가 접속하더라도, 이전의 정보가 남아있지 않아서 클라이언트의 정보에 대한 비교가 불가능 합니다.
- 장점
세션 (HTTP Session)
▶가상의 접속 상태를 유지하기 위한 메카니즘입니다.
- HTTP세션이 종료되는 시점은, 클라이언트 세션 아이디가 무효화 되는 시점입니다. 이 시점은 로그 아웃 등을 할 때 종료된다고 보면 됩니다.
- 웹에서 세션을 사용하고 있다면, 반드시 세션을 종료하는(로그아웃) 방법도 제공을 해주어야 합니다.
- 일정 시간 동안 동일한 클라이언트로부터 요청이 발생하지 않는다면, 이 또한 세션이 종료되도록 할 수 있습니다. web.xml에 설정된 경우
의 설정을 통해 시간 설정이 가능합니다.
위의 설정대로 한다면, Session 은 30분후에 종료가 되겠다는 뜻이구요, 아파치 웹 서버를 따로 구성했을 경우에는 아파치 웹 서버의 WEB.XML을 수정해야 합니다.
쿠키
▶쿠키라는 것은 클라이언트의 상태 정보를 서버로 전송하기 위해 사용할 수 있는 방법 중의 하나로서 클라이언트에 저장되는 단순한 텍스트 입니다.
- 쿠키는 브라우저가 종료되더라도 정보가 사라지지 않습니다.
- 쿠키는 사용자가 컴퓨터를 켜든 끄든 하드디스크에 (상당기간) 저장되어 있습니다
- 왜? PC에 저장하는 걸까요?
- 그것은 HTTP 프로토콜이 'stateless' 프로토콜이기 때문입니다. 웹 브라우저가 웹 서버에 접속을 해서 어떤 문서나 파일을 요청하면 웹 서버는 요청 받은 내용을 보내준 다음 접속을 끊습니다.
즉, 접속을 한 '상태(state)'가 지속되지 않고 요청된 것만 처리한 뒤 연결을 끊는 거죠. 그러므로 웹 서버는 일단 요청된 내용들을 클라이언트에 보내고 나면 그 뒤 사용자가 접속을 하고 있는지 어떤지 알 수가 없습니다. - 나아가, 예전에 접속했던 클라이언트가 또 접속을 한 것인지 아닌지 등은 더더욱 알 수 없습니다. 그런데 웹 사이트를 운용하는 측에서는 어떤 사용자가 다시 방문을 했는지 와 같은 정보가 절실히 필요했고 바로 이런 점을 해결하기 위해, 즉 stateless한 http의 특징을 커버하기 위해 등장한 아이디어가 쿠키(Cookie)입니다.
- 쿠키의 아이디어는 간단합니다. 접속한 클라이언트의 하드디스크에 적당한 정보를 저장해 둠으로써 또 그 클라이언트가 접속한 경우 언제든지 하드디스크에 저장된 정보를 읽어 들여서 그 사용자를 인식할 수 있는 것입니다. '
- 상태'에 관한 점검을 언제든지 할 수 있는 것이죠.
- 쿠키에 저장되는 내용은 천차만별입니다. 간단하게는, 사용자가 어떤 페이지를 읽었고, 로그인 아이디가 뭐고, 이 메일 주소가 뭐고 등을 기록할 수도 있고, 사용자가 어떤 물품을 주문했는지, ip 주소가 뭐고, 어떤 사이트를 거쳐서 우리 사이트로 왔는지, 또는 서버에서 각 클라이언트를 식별할 특별한 정보를 기록하는 등, 거의 모든 형태의 정보를 저장할 수 있습니다.
- 사용자 처지에서는 사실 기분 나쁠 수 있습니다. 나도 모르게 나의 행동이 하나하나 기록되어 '파일'로 저장되고 있고, 그 파일이 다른 곳도 아닌 '내' 컴퓨터에 나도 모르게 저장된다는 것은 별로 좋은 느낌은 아니죠.
- 하지만, 쿠키 파일은 사용자가 컴퓨터를 끄든 켜든 하드디스크에 (상당 기간) 저장되어 있기 때문에, 언제든지 사용자가 다시 어떤 웹 사이트에 접속하면 쿠키에 저장해 놓은 정보를 읽어 들여서 여러 형태의 '맞춤화된' 서비스를 제공할 수 있습니다. 이를 테면, 로그인을 한 번만 하면 그 다음부터 안 해도 된다든지, 어떤 페이지를 "몇 번 보셨군요" 라고 알려준다든지 등이 가능합니다.
- 그것은 HTTP 프로토콜이 'stateless' 프로토콜이기 때문입니다. 웹 브라우저가 웹 서버에 접속을 해서 어떤 문서나 파일을 요청하면 웹 서버는 요청 받은 내용을 보내준 다음 접속을 끊습니다.
- JSP에서 쿠키 설정하는 방법
- 헤더에 직접 지정하는 방법
- HTTP 헤더를 이용한 쿠키 설정
- Set-Cookie : name = value; expires = date; domain = serverDomain;
- path = serverPath; secure(HTTPS)
- 쿠키를 추상화 시켜놓은 서블릿 API를 이용하는 방법
- 이것은 보다 객체 지향적인 방법으로 클라이언트와 서버간의 쿠키 데이터 전송을 위해 Set-Cookie와 쿠키 헤더를 추상화한 javax.servlet.http.Cookie 클래스를 사용합니다.
- 헤더에 직접 지정하는 방법
JSP에서의 세션 관리
▶HttpSession과 세션 관리
- HttpSession은 서블릿/JSP에서 세션을 유지하기 위해 필요한 일련의 작업을 단순화 혹은 추상화시켜 놓은 API라고 생각하면 될 것입니다.
- 세션을 유지할 때는 다음의 방법을 사용합니다.
- 최초의 클라이언트에서 요청할 때 세션 유지를 위해 HttpSession 객체를 생성하고 고유한 세션 아이디를 생성합니다.
- HttpSession session = request.getSession();
- getSession()메소드는 create가 true일 경우 클라이언트 요청에 대해 기존에 생성된 객체가 존재할 때는 해당 객체를 리턴하고, 그렇지 않으면 새로운 객체를 생성하여 리턴합니다.
- create가 false 인 경우에 요청에 대해 생성된 HttpSession객체가 존재할때 이를 리턴하고, 그렇지 않으면 null 을 리턴합니다.
- 일반적으로 getSession()메소드는 getSession(true)과 동일하다고 보면 도비니다.
- HttpSession 객체 생성시 자동으로 생성된 세션 아이디를 이후의 클라이언트 요청부터 요청에 포함하여 전송합니다.
- 세션이 시작된 이후의 모든 요청이 들어올 때 마다 서버가 클라이언트에 부여한 세션 아이디를 통해 세션을 유지하도록 합니다.
- 최초의 클라이언트에서 요청할 때 세션 유지를 위해 HttpSession 객체를 생성하고 고유한 세션 아이디를 생성합니다.
- [세션 테스트]
차암 쉽쬬잉~~~~~~~~~♬
★만약, HttpSession을 사용하지 않고, page 속성에서 session = true로 했을 경우에는 JSP 내장 Session객체를 사용함을 의미합니다. 이 때에 HttpSession session 할 경우에는 동일한 session 이름의 객체가 생성되므로 HttpSession으로 만든 session에 오류가 날 수 있습니다. 이 때에는 HttpSession의 이름을 mySession 이나 뭐 기타 등등으로 바꾸면 됩니다.
★ 혹은, HttpSession을 쓰지 않고, JSP 내장 객체의 session을 사용하면 되겠는데, 이 때 jsp 내장객체의 session객체는 isNew()라는 메소드를 제공함으로써 세션의 존재 유무 혹은 세션이 현재 페이지에서 만들어졌는지 등을 알 수 있습니다.
HttpSessionBindingListener
jsp, 자바 빈( scope=application ) 하는 경우입니다.
굳이 db에 테이블을 생성하지 않아도 접속자 수를 실시간으로 구하실 수 있으실 겁니다.
=== SessionChecker.java ===============================
import java.io.*;
import java.util.*;
import javax.servlet.http.*;
public class SessionChecker{
public void setSession(HttpSession session){
// 리스너 객체를 생성해서 이것도 세션에 같이 담는다. 리스너 라는 이름으로...
session.setAttribute("listener", new CustomBindingListener());
}
}
// 여기서 구현했습니다..
class CustomBindingListener implements HttpSessionBindingListener {
public void valueBound(HttpSessionBindingEvent event) {
// 세션이 생겼을 할 내용
System.out.println("BOUND as " + event.getName() + " to " + event.getSession().getId());
}
public void valueUnbound(HttpSessionBindingEvent event) {
// 세션이 종료되었을때
System.out.println("UNBOUND as " + event.getName() + " to " + event.getSession().getId());
}
}
==== test.jsp =========================================
<%@ page contentType="text/html;charset=KS_C_5601-1987" %>
<%
session.setMaxInactiveInterval(60); // 걍 결과가 빨리 보고싶어서여.. 60초
sc.setSession(session);
out.println("세션 등록");
%>
test 페이지를 연 후 60초 뒤에 unbound 어쩌구의 메세지를 보실 수 있을 겁니다.
[출처 : OKJSP > http://www.okjsp.pe.kr/seq/20524]
출처 : http://blog.naver.com/korekiss/20038228350
HttpSessionBindingListener 는 웹에서 동시 사용자의 수 또는 하나의 아이디로 동시접속을 제한 할때 유용한 인터페이스 이다. HttpSessionBindingListener 는 두개의 메소드를 지니는데 valueBound() 와 valueUnbound() 메소드 이다.
package itexpert.chap05;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.util.Hashtable;
import java.util.Enumeration;
public class LoginManager implements HttpSessionBindingListener
{
private static LoginManager loginManager = null
private static Hashtable loginUsers = new Hashtable();
private LoginManager(){
super();
}
public static synchronized LoginManager getInstance(){
if(loginManager == null){
loginManager = new LoginManager();
}
return loginManager
}
//아이디가 맞는지 체크
public boolean isValid(String userID, String userPW){
return true //자세한 로직은 미구현
}
//해당 세션에 이미 로그인 되있는지 체크
public boolean isLogin(String sessionID){ //세션 ID를 받습니다.
boolean isLogin = false
Enumeration e = loginUsers.keys();
String key = ""
while(e.hasMoreElements()){ //다음 데이터의 유무를 판단합니다.
key = (String)e.nextElement();//key변수에 대입.
if(sessionID.equals(key)){ //현재 세션 ID값과 로그인한 값을 비교합니다.
isLogin = true //해당되는 세션 아이디가 이미 등록되어 있다는 것을 의미.
}
}
return isLogin; //초기는 FALSE 이지요. WHILE문에서 걸러진다면 위에서 알아서 처리되겠습니다.
}
//중복 로그인 막기 위해 아이디 사용중인지 체크
public boolean isUsing(String userID){
boolean isUsing = false
Enumeration e = loginUsers.keys();
String key = ""
while(e.hasMoreElements()){
key = (String)e.nextElement();
if(userID.equals(loginUsers.get(key))){//hashTable에 저장된 키값을 받는다는 것은
//키 값에 포함된 Value 값을 받는 것(즉, 사용자ID)
isUsing = true
}
}
return isUsing;
}
//세션 생성
public void setSession(HttpSession session, String userID){
loginUsers.put(session.getId(), userID) //[1] 세션 ID를 가져오고, [2] 사용자 ID를 가져와서 대입.
session.setAttribute("login", this.getInstance()); //세션의 속성에 로그인매니져의 정보를 설정합니다.
}
//세션 성립될 때
public void valueBound(HttpSessionBindingEvent event){ // 로그인 할때
}
//세션 끊길때
public void valueUnbound(HttpSessionBindingEvent event){ //로그아웃 할때
loginUsers.remove(event.getSession().getId());//이벤트가 발생을 시킨 세션의 ID 값을 가져와서 HashTable
//데이터를 삭제합니다.
}
//세션 ID로 로긴된 ID 구분
public String getUserID(String sessionID){
return (String)loginUsers.get(sessionID);
}
//현재 접속자수
public int getUserCount(){ //몇 명의 사용자가 로그인 하고 있는지 계산합니다.
return loginUsers.size();
}
}
<%@ page contentType="text/html;charset=euc-kr" import="itexpert.chap05.LoginManager"%>
<% LoginManager loginManager = LoginManager.getInstance(); %>
<html>
<body>
<center>
현재 접속자수 : <%= loginManager.getUserCount() %><p>
<hr>
<%
if(loginManager.isLogin(session.getId())){ //세션 아이디가 로그인 중이면
out.println(loginManager.getUserID(session.getId())+"님 안녕하세요
"
+"로그아웃");
}
else{ //그렇지 않으면 로그인 할 수 있도록
%>
<form name="login" action="Bind_login_ok.jsp">
아이디: <input type="text" name="userID"><br>
비밀번회: <input type="text" name="userPW"><br>
<input type="submit" value="로그인">
form>
<% }%>
center>
body>
html>
<%@ page contentType="text/html;charset=euc-kr" import="itexpert.chap05.LoginManager"%>
<% LoginManager loginManager = LoginManager.getInstance(); %>
<%
request.setCharacterEncoding("euc-kr");
String userID = request.getParameter("userID");
String userPW = request.getParameter("userPW");
if(loginManager.isValid(userID, userPW)){ //ID와 비밀번호가 맞는지 확인합니다.
if(!loginManager.isUsing(userID)){//ID가 사용중인지 아닌지 판단합니다.
loginManager.setSession(session, userID); // 사용하지 않는다면, 현재 세션과 ID 저장
response.sendRedirect("Bind_login.jsp"); //다시 로그인 화면으로 이동합니다.
}
else{
//throw new Exception("이미 로그인중입니다.");
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
}
else{
//throw new Exception("ID/PW 이상");
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
%>
<%@ page contentType="text/html;charset=euc-kr"%>
<%
session.invalidate();
response.sendRedirect("Bind_login.jsp");
%>
<%@ page language="java" contentType="text/html; charset=EUC-KR" import="itexpert.chap05.LoginManager"
pageEncoding="EUC-KR"%>
DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
LoginManager loginManager = LoginManager.getInstance();
%>
<script>
function isLoginUserForm() {
if(login.userID.value == "") {
alert ("ID를 입력하지 않았습니다.");
login.userID.focus();
return false
}
if(login.userPW.value == "") {
alert ("패스워드를 입력하지 않았습니다.");
login.userPW.focus();
return false
}
return true
}
script>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>방명록 작성 실습 예제 v0.01title>
head>
<body>
<center>
<h1> 방명록 작성 실습 예제 v0.03(JSP+JDBC + Login) h1>
center>
<hr/>
본 실습을 문서를 참고하지 않고 다시 작성할 수 있을때까지 반복하여 수행한다.
<br>
<hr/>
<a href="guestController.do?action_code=insert&mode=1"> 방명록작성(JSP를 이용하여 오라클 DB에 자료를 입력하는 실습)a>
<hr/>
<a href="guestController.do?action_code=select"> 방명록보기(JSP를 이용하여 오라클 DB를 조회하는 실습)a>
<hr/>
<h3>현재 접속자 수 : <%= loginManager.getUserCount() %> h3>
<hr>
<%
if( loginManager.isLogin(session.getId())) {
%>
<%= loginManager.getUserID(session.getId()) %> 님 안녕하세요
<br>
<a href="Bind_logout.jsp"> 로그아웃 a>
<%
} else {
%>
<form name="login" action="Bind_login_ok.jsp" onsubmit="return isLoginUserForm();" >
아이디 : <input type="text" name="userID"> 비밀번호 : <input type="password" name="userPW">
<input type="submit" value="로그인" >
form>
현재 버전에서의 아이디와 패스워드는 데이터베이스를 점검하지 않고 있음. <p>
그러나 동일한 이름의 아이디를 이용한 중복 로그인은 허용하지 않음.
<%
}
%>
body>
html>