Session

Byung Seon Kang·2022년 10월 3일
0

서블릿에 대해

목록 보기
5/9

이 글은 Head First Servlet and JSP 책을 기반으로 작성하였습니다.

웹서버는 요청이 누구에게 왔는지 기억하지 않는다. 즉, 과거에 어떤 요청을 보냈는지 모른다. 하지만 현재 요청이 이전 요청들의 값들을 필요로한다면 어떻게 해야할까?(conversational state).

  • 우선 여기선 인증이라는 것을 제외하고 봅니다.
    • 인증은 차후 security쪽에서.
    • 여기서는 유저의 식별을 어떻게 할거냐?를 중점으로 보기때문에 session ID 관련해서만 봅니다.

client의 이전 요청들을 추적하는 방법

1. Stateful Session Enterprise Javabean 사용

  • 각 요청마다 request는 각 client의 stateful bean에 위치하게 될 것.
    즉, conversational state를 저장 가능하다.
  • 하지만 너무 많은 오버헤드가 발생한다(메모리 너무 많이 잡아먹음)
  • 게다가 tomcat은 J2EE 서버의 기능을 전부 제공하지 않기때문에 애초에 안됨.

2. Database 사용

  • 역시나 작동한다.
  • 클라이언트의 데이터를 데이터베이스에 두고 사용.
  • 하지만 역시나 오버헤드가 크다

3. HttpSession 사용

  • 여러 요청에서 conversational state를 저장한다.
  • 사실 database나 session bean을 사용해도 이녀석을 쓰게 됨.
  • 특정 database key나 session bean ID등을 식별자로 사용하게 됨.

자 그러면 HttpSession을 쓰기로는 했는데.. 어떻게 유저를 식별할까?

  • 컨테이너는 어떤 요청이든 새로운 유저로 받아들이는데!
  • IP주소로 인식하게 되면?
    • local IP network는 당연히 유니크하다.
    • 하지만, 서버에서는 local IP network를 인식하지 않는다.
      라우터의 주소를 인식한다!
      그래서 다른 사람들과 동일한 IP 주소값을 보이게 되는 문제가 발생.
      참고
      • request.getRemoteAddr() 의 경우에는 가장 마지막 프록시 주소를 리턴
      • request.getHeader("X-FORWARDED-FOR") 는 맨 처음 유저 IP 리턴.
        • X-Forwarded-For: client, proxy1, proxy2
        • 이와 같이 comma를 통해서 proxy 주소까지 저장한다고 합니다.
          참고사이트
    • X-FORWARDED-FOR 사용하는 경우를 제외하고 우선 서버입장에서 봤을 때 결국 IP 주소값을 사용하는것이 옳다고 보기 힘들다.
  • HTTPS를 사용하게 된다면?
    • 로그인을 하고 secure connection을 사용하게 되면 session과 함께 인식이 가능.
      하지만 좋은 웹사이트 디자인은 반드시 로그인이 필요하지 않을경우 이를 강제하지 않는다고 합니다.
    • 그래서 인증된 클라이언트가 반드시 필요한 경우가 아니면 단순히 session을 통해 전달하는 것이 좋다고 합니다.

그럼 어떻게 식별할거냐?

  • 답은 session ID.
  • 쿠키를 통해 session ID를 주고받는다.
response
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=0AAB6C8DE415
Content-Type: text/html
...
request
POST /select/selectCookie.do HTTP/1.1
Host: www.~~~.com
User-Agent: Chrome/~~
Cookie: JSESSIONID=0AAB6C8DE114
...

세션 사용방법.

  • 컨테이너는 항상 쿠키가 작동한다고 믿는다.
    HttpSession session = request.getSession();
    얘 쓰면 아래와 같은 작업들을 자동으로 수행
    • HttpSession object 생성
    • unique session ID 생성
    • session ID를 쿠키에 넣는 작업
    • Cookie를 response에 넣는 작업
  • 내부적으로 일을 수행할 때 이렇게 한다.
    • 만약 쿠키에 session 담겨 있었다면 그대로 쓰고
    • 안담겨있으면 새로 unique session ID 만들어준다.

추가

  • getSession(false)를 사용하게 되면 기존에 session ID 있는것만 사용.
    없으면 새로 생성하지 않는다.

만약 client browser에서 cookie를 받아들이지 않는다면?

  • 서버에서는 이를 모른다.
    • 뭐 오류같은 것도 클라에서 안보내주니까.
  • 컨테이너에서 추가적인 설정을 해줘야한다고 합니다..
  • 아니면 URL에서 session ID를 취급해줘도 됩니다.
    URL + ;jsessionid=123145151

어떻게 session 제거할까

  • 서버에서 세션정보를 저장하는 것은 리소스 소모.
    • 언젠간 삭제해야한다.
  • 언제 어떻게 삭제?
  • 기간 설정 가능하다.
int getMaxInactiveInterval();
long getCreationTime();
long getLastAccessedTime();
void invalidate();
...

요런거 활용해서..

  • session time out은 DD에서도 되고, 특정 session에서 설정가능하기도 함.

Session Migration

  • distributed web app인 경우 어떻게?
    • load-balancing을 통해 다른 JVM들에 각각 요청 보내주게 될 것.
  • 이렇게 되면 문제가 있다.
    • session의 정보를 알고 있던 VM이 아닌 다른 VM에 이 요청이 가게 되는 경우가 존재
  • 이렇게 되면 HttpSession object를 그 다른 VM으로 옮겨야 한다.
    • 이걸 Session Migration이라고 함.
  • Serialize가 가능한 속성과 불가능한 속성에 대해 또 다른 방식으로 옮겨야 함.
    • Serializable이면 그냥 HttpSession
    • not Serializable이면 HttpSessionActivationListener 구현해서 씀.
    • 95%정도는 Serializable이라 거의 볼 일 없다고 합니다.

코드 예시

  1. sessionAttributeListener

package com.example.servlet;

import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionBindingEvent;

public class BeerAttributeListener implements HttpSessionAttributeListener {
    public void attributeAdded(HttpSessionBindingEvent event) {

        String name = event.getName();
        Object value = event.getValue();

        System.out.println("Attribute added: " + name + ": " + value);
    }

    public void attributeRemoved(HttpSessionBindingEvent event) {
        String name = event.getName();
        Object value = event.getValue();
        System.out.println("Attribute removed: " + name + ": " + value);
    }

    public void attributeReplaced(HttpSessionBindingEvent event) {
        String name = event.getName();
        Object value = event.getValue();
        System.out.println("Attribute replaced: " + name + ": " + value);
    }
}
  1. HttpSessionListener
package com.example.servlet;

import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;

public class BeerSessionCounter implements HttpSessionListener {

    static private int activeSessions;

    public static int getActiveSessions(){
        return activeSessions;
    }

    public void sessionCreated(HttpSessionEvent event){
        activeSessions++;
    }

    public void sessionDestroyed(HttpSessionEvent event) {
        activeSessions--;
    }
}
  1. Serializable, 그리고 not Serializable한 Dog 객체에 대해 session migration을 고려한 코드
package com.example.servlet;

import jakarta.servlet.http.*;

import java.io.Serializable;

public class Dog implements HttpSessionBindingListener,
                                HttpSessionActivationListener, Serializable {
    private String breed;

    public Dog(String breed){
        this.breed = breed;
    }

    public String getBreed(){
        return breed;
    }

    public void valueBound (HttpSessionBindingEvent event){
        System.out.println("I'm in session now");
    }

    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println("I'm not in session now");
    }

    public void sessionWillPassivate(HttpSessionEvent event) {
        //non-serializable
    }

    public void sessionDidActivate(HttpSessionEvent event){
        //code to restore my fields
        //in sessionWillPassivate.
    }
}
profile
왜 필요한지 질문하기

0개의 댓글