Spring ‐ 쿠키와 세션 - dnwls16071/Backend_Study_TIL GitHub Wiki

📚 쿠키(Cookie)

  • 쿠키의 종류 : 영속 쿠키 / 세션 쿠키

    • 영속 쿠키 : 만료 날짜 입력시 해당 날짜까지 유지
    • 세션 쿠키 : 만료 날짜 생략시 브라우저 종료시까지 유지
  • 쿠키의 보안 문제

    • 쿠키 값은 임의로 변경할 수 있다. 결국 쿠키의 값을 변경하게 되면 한순간에 다른 사용자로 인식이 된다.
    • 쿠키에 보관된 값은 훔쳐갈 수 있다. 훔친 쿠키를 통해 악의적인 요청을 계속 보낼 수 있다.

📚 세션(Session)

@PostMapping("/login")
public String loginV4(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult,
                      @RequestParam(defaultValue = "/") String redirectURL,
                      HttpServletRequest request) {

    if (bindingResult.hasErrors()) {
        return "login/loginForm";
    }

    Member loginMember = loginService.login(form.getLoginId(), form.getPassword());

    if (loginMember == null) {
        bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
        return "login/loginForm";
    }

    //로그인 성공 처리
    //세션이 있으면 있는 세션 반환, 없으면 신규 세션을 생성
    HttpSession session = request.getSession();
    //세션에 로그인 회원 정보 보관
    session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
    return "redirect:" + redirectURL;
}
  • 서블릿(Servlet) : 클라이언트의 HTTP 요청을 받아 처리하고, 동적으로 HTTP 응답을 생성하는 Java 기반의 서버 사이드 컴포넌트
    • HttpServletRequest.getSession(true)
      • 세션이 있으면 세션을 반환하나 세션이 없다면 세션을 생성해 반환한다.
      • 기본적으로 true이므로 생략해도 무방하다.
    • HttpServletRequest.getSession(false)
      • 세션이 있으면 기존 세션을 반환하고 없으면 null을 반환한다.
    • setAttribute()
      • getSesison()으로 가져온 세션에 대해서 값을 키-값 쌍으로 넣는다.
- setAttribute() 메서드를 사용하면 하나의 세션에 여러 값을 저장할 수 있게 된다.
- 로그인 처리가 완료되면 Response에 자동으로 쿠키 값으로 JSESSIONID라는 것이 같이 담기게 된다.
- JSESSIONID는 내장 톰켓 서버와 같은 WAS에서 자동으로 발급해주는 식별자이다. 이는 HTTP의 Stateless 특성을 보완하기 위해 각 클라이언트를 구분하는 용도로 사용된다.
- 실제 발급되는 세션의 경우 서버 측의 세션 저장소라는 곳에서 보관한다. 이 저장소는 클라이언트 측에서 접근할 수 없다.

스크린샷 2025-01-29 오전 2 51 41

  • 세션ID를 응답 쿠키로 만들어 반환하면 클라이언트 측 쿠키 저장소에서 이를 보관한다.
  • 서버 측에서는 만든 세션ID에 대한 것을 세션 저장소에 저장한다.

스크린샷 2025-01-29 오전 2 53 25

  • 클라이언트 측의 쿠키 저장소에서 보관하는 쿠키를 서버 측 요청을 보낼 때 함께 포함해서 보내게 된다.
  • 그렇게 되면 서버 측에서 해당 쿠키 정보를 열람해 세션 ID를 서버 측 세션 저장소와 비교해 조회한다.
@PostMapping("/logout")
public String logoutV3(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session != null) {
        session.invalidate();
    }
    return "redirect:/";
}
  • invalidate() : 해당 세션을 제거한다.

📚 서블릿 HTTP 세션

  • 서블릿은 세션을 위해 HttpSession이라는 기능을 제공한다.
@GetMapping("/session-info")
public void sessionInfo(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session == null) {
        return "세션이 없습니다.";
    }
    //세션 데이터 출력
    session.getAttributeNames().asIterator()
            .forEachRemaining(name -> log.info("session name={}, value={}", name, session.getAttribute(name)));

    log.info("sessionId={}", session.getId());
    log.info("getMaxInactiveInterval={}", session.getMaxInactiveInterval());
    log.info("creationTime={}", new Date(session.getCreationTime()));
    log.info("lastAccessedTime={}", new Date(session.getLastAccessedTime()));
    log.info("isNew={}", session.isNew());
}
  • sessionId : 세션 ID, JSESSIONID 값
  • maxInactiveInterval : 세션 유효시간
  • creationTime : 세션 생성일시
  • lastAccessdTime : 세션과 연결된 사용자의 마지막 서버 접근 시간
  • isNew : 새로 생성된 세션인가 아니면 클라이언트에서 서버로 JSESSIONID를 요청해서 조회한 세션인지 여부
    • 서버 측 세션 저장소의 키 값은 JSESSIONID이다.

❗세션은 사용자가 로그아웃을 호출해야 세션이 삭제되면서 관리가 된다. 허나 대부분의 사용자는 로그아웃을 하지 않고 브라우저를 종료하기 때문에 세션이 그대로 남아있게 된다. ❗세션도 결국 자원이기에 타임아웃을 설정해 세션 자원을 관리할 필요가 있다.

[ 다중 서버에서의 세션 관리 ]

  • 하나의 서버를 운영할 때 문제가 없으나 만약 다중 서버로 운영할 경우 세션 저장소는 하나의 서버에 하나의 세션 저장소로 대응되기 때문에 문제가 생긴다.
  • 여러 서버를 띄워 사용하는 경우 세션 저장소를 별도로 구성해 여러 서버가 단 하나의 세션 저장소만을 가리키도록 해야 한다.

세션 공유 참고문서