본문 바로가기

Spring Boot

Spring-Boot Cookie Session

📌 쿠키 Cookie

웹사이트를 방문할 때 클라이언트(브라우저) 로컬에 저장되는 키와 값이 들어있는 작은 데이터 조각이다.


쿠키 종류

  • 영속 쿠키 : 만료 날짜를 입력하면 해당 날짜까지 유지
  • 세션 쿠키 : 만료 날짜를 생략하면 브라우저 종료시 까지만 유지

쿠키 동작 방식 

  1. 클라이언트가 페이지를 요청
  2. 서버에서 쿠키를 생성
  3. HTTP 헤더에 쿠키를 포함 시켜 응답
  4. 브라우저가 종료되어도 쿠키 만료 기간이 있다면 클라이언트에서 보관하고 있음
  5. 같은 요청을 할 경우 HTTP 헤더에 쿠키를 함께 보냄
  6. 서버에서 쿠키를 읽어 이전 상태 정보를 변경 할 필요가 있을 때 쿠키를 업데이트 하여 변경된 쿠키를 HTTP 헤더에 포함시켜 응답

📒쿠키 생성

  • 로그인에 성공하면 쿠키를 생성하고 HttpServletResponse 에 담는다.
  • 쿠키 이름은 memberId 이고, 값은 회원의 id 를 담아둔다.  Cookie(name, value) 
  • 웹 브라우저는 종료 전까지 회원의 id 를 서버에 계속 보내줄 것이다.
Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
response.addCookie(idCookie);

 

📒 쿠키 확인하기 

  • @CookieValue 를 사용하면 name으로 편리하게 쿠키를 조회할 수 있다.
  • 로그인 하지 않은 사용자도 홈에 접근할 수 있기 때문에 required = false 를 사용한다.
 @GetMapping("/")
 public String homeLogin(@CookieValue(name = "memberId", required = false) Long memberId, Model model) {
 
 // memberId가 없으면 다시 로그인창으로 
 if (memberId == null) {return "home";}
 
 //로그인
 Member loginMember = memberRepository.findById(memberId);
 if (loginMember == null) {return "home";}
 
 return "loginHome";
 }

 

실행 

로그인에 성공시 세션 쿠키 가 지속해서 유지되고, 웹 브라우저에서 서버에 요청시 memberId 쿠키를 계속 보내준다.

 

📒로그아웃

  • 서버에서 해당 쿠키의 종료 날짜를 0으로 지정 해당 쿠키는 즉시 종료된다.
@PostMapping("/logout")
public String logout(HttpServletResponse response) {
 expireCookie(response, "memberId");
 return "redirect:/";
}
private void expireCookie(HttpServletResponse response, String cookieName) {
 Cookie cookie = new Cookie(cookieName, null);
 // 쿠키 종료
 cookie.setMaxAge(0);
 response.addCookie(cookie);
}

 


📌 쿠키만을 이용하면 로그인의 보안 문제가 이러난다.

  • 쿠키 값은 임의로 변경할 수 있다.
    • 클라이언트가 쿠키를 강제로 변경하면 다른 사용자가 된다.
  • 쿠키에 보관된 정보는 훔쳐갈 수 있다.
    • 이 정보가 웹 브라우저에도 보관되고, 네트워크 요청마다 계속 클라이언트에서 서버로 전달된다.
  • 해커가 쿠키를 한번 훔쳐가면 평생 사용할 수 있다.
    • 해커가 쿠키를 훔쳐가서 그 쿠키로 악의적인 요청을 계속 시도할 수 있다.

📌 대안점은 ?

  • 쿠키에 예측 불가능한 임의의 토큰(랜덤값)을 노출해야 한다.
  • 해커가 토큰을 털어가도 시간이 지나면 사용할 수 없도록 서버에서 해당 토큰의 만료시간을 짧게 유지하게 한다.
  • 해킹이 의심되는 경우 서버에서 해당 토큰을 강제로 제거하면 된다.
쿠키 + 세션 조합으로 위 보안 문제를 해결 할 수 있다.

 

📒 쿠기와 세션 차이

쿠키는 클라이언트 쪽에 저장이 되어 웹 페이지를 닫아도 저장이 되있고 세션은 서버 쪽에 저정이되어 웹 페이지를 닫으면 저장이 안됩니다.
  • 쿠키 
    • 저장위치 : 클라이언트
    • 사용 자원 : 클라이언트의 리소스
    • 속도 : 세션보다 빠르다.
    • 보안 : 취약
    • 저장형식 : Text
  • 세션
    • 저장위치 : 서버
    • 사용 자원 : 서버의 리소스
    • 속도 : 쿠키보다 느림
    • 보안 : 좋음
    • 저장형식 : Object

 

📌 세션 Session

세션은 사용자가 웹사이트와 상호작용하는 동안 일시적으로 정보를 저장하여, 사용자 상태를 유지하는 방법이다.


세션 동작 방식 

  1. 클라이언트가 서버에 접속 시 세션 ID를 발급 받음
  2. 클라이언트는 세션 ID에 대해 쿠키를 사용해서 저장하고 가지고 있음
  3. 클라리언트는 서버에 요청할 때, 이 쿠키의 세션 ID를 같이 서버에 전달해서 요청
  4. 서버는 세션 ID를 전달 받아서 별다른 작업없이 세션 ID로 세션에 있는 클라언트 정보를 가져와서 사용
  5. 클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답

 

📒세션을 이용한 로그인

서블릿이 제공하는 세션 기능 사용하기

  • 서블릿은 세션을 위해 HttpSession이라는 기능을 제공해준다.
  • 서블릿을 통해 HttpSession을 생성하면 쿠키 이름이 JSESSIONID 이고, 값은 추정 불가능한 랜덤 값이다.
    • Cookie: JSESSIONID=5B78E23B513F50164D6FDD8C97B0AD05
  • 아래 코드는 세션을 생성하고, 세션에 정보를 보관하는 기능이다.
 //세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
 HttpSession session = request.getSession();
 //세션에 로그인 회원 정보 보관
 session.setAttribute("loginMember", loginMember);
  • request.getSession() 에 인자로 true, false를 줄 수 있다. default = true
    • true : 세션이 있으면 기존 세션 반환, 없으면 새로 생성 후 반환
    • false : 세션이 있으면 기존 세션 반환 없으면 NULL 반환

📌이해 하기

  1. 세션 생성
    • HttpSession session = request.getSession(); 
    • 세션은 서버 측에 저장되며, 서버는 해당 세션을 식별할 수 있는 고유한 세션 ID를 생성합니다.
  2. 세션에 데이터 저장 
    • session.setAttribute("loginMember", loginMember);
    • 이 코드는 loginMember 객체를 세션에 저장합니다. 이 데이터는 서버에 저장되며, 세션 ID를 통해 나중에 참조할 수 있습니다.
  3. 세션 ID 쿠키에 저장
    • 서버는 생성된 세션 ID를 클라이언트에 쿠키 형태로 전달합니다. 이 쿠키는 기본적으로 JSESSIONID라는 이름으로 저장됩니다.
    • 예를 들어, 클라이언트의 브라우저에 JSESSIONID=xyz12345와 같은 쿠키가 저장됩니다.
  4. 후속 요청에서 세션 사용
    • 사용자가 같은 웹사이트에 다시 요청을 보낼 때, 브라우저는 JSESSIONID 쿠키를 서버로 전송합니다.
    • 서버는 이 세션 ID를 사용해 저장된 세션 데이터를 찾아 사용합니다. 이 경우, loginMember 정보가 세션에서 불러와져 사용됩니다.
요약하면, 세션 데이터는 서버에 저장되고, 클라이언트의 브라우저에는 세션을 식별하기 위한 세션 ID만 쿠키로 저장됩니다. 브라우저는 이후 요청 시 이 세션 ID를 서버에 보내고, 서버는 이를 통해 세션 데이터를 식별하여 사용합니다.

📒세션 조회

  • request.getSession(false)이므로 세션이 없으면 NULL이 반환되어 다시 로그인 창으로 보냄
HttpSession session = request.getSession(false);
 if (session == null) return "home";

 Member loginMember = (Member) session.getAttribute("loginMember");
 //세션에 회원 데이터가 없으면 home
 if (loginMember == null) return "home";

📒로그아웃

session.invalidate() : 세션을 제거한다.
@PostMapping("/logout")
public String logout(HttpServletRequest request) {
 //세션을 삭제한다.
 HttpSession session = request.getSession(false);
 if (session != null) {session.invalidate();}
 return "redirect:/";
}

 

📌세션 여부 체크

  • sessionId : 세션Id, JSESSIONID 의 값이다. 예) 34B14F008AA3527C9F8ED620EFD7A4E1
  • maxInactiveInterval : 세션의 유효 시간, 예) 1800초, (30분)
  • creationTime : 세션 생성일시
  • lastAccessedTime : 세션과 연결된 사용자가 최근에 서버에 접근한 시간, 클라이언트에서 서버로 sessionId (JSESSIONID )를 요청한 경우에 갱신된다.
  • isNew : 새로 생성된 세션인지, 아니면 이미 과거에 만들어졌고, 클라이언트에서 서버로 sessionId ( JSESSIONID )를 요청해서 조회된 세션인지 여부
@GetMapping("/session-info") // 세션이 있나 없나 체크하는 맵핑
    public String 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("maxInactiveInterval={}", session.getMaxInactiveInterval());
        log.info("creationTime={}", new Date(session.getCreationTime()));
        log.info("lastAccessedTime={}", new Date(session.getLastAccessedTime()));
        log.info("isNew={}", session.isNew());

        return "세션 출력";
    }

 

실행 결과

참고 자료

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 참고


 

'Spring Boot' 카테고리의 다른 글

Spring-Boot @Transactional 트랜잭션 전파  (0) 2024.09.15
Spring-Boot H2 데이터베이스 설정과 연결  (1) 2024.09.02
Spring-Boot MyBatis  (0) 2024.09.01
Spring-Boot Interceptor  (36) 2024.07.26
Spring-Boot Bean validationn  (0) 2024.07.25