📌 쿠키 Cookie
웹사이트를 방문할 때 클라이언트(브라우저) 로컬에 저장되는 키와 값이 들어있는 작은 데이터 조각이다.
쿠키 종류
- 영속 쿠키 : 만료 날짜를 입력하면 해당 날짜까지 유지
- 세션 쿠키 : 만료 날짜를 생략하면 브라우저 종료시 까지만 유지
쿠키 동작 방식
- 클라이언트가 페이지를 요청
- 서버에서 쿠키를 생성
- HTTP 헤더에 쿠키를 포함 시켜 응답
- 브라우저가 종료되어도 쿠키 만료 기간이 있다면 클라이언트에서 보관하고 있음
- 같은 요청을 할 경우 HTTP 헤더에 쿠키를 함께 보냄
- 서버에서 쿠키를 읽어 이전 상태 정보를 변경 할 필요가 있을 때 쿠키를 업데이트 하여 변경된 쿠키를 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
세션은 사용자가 웹사이트와 상호작용하는 동안 일시적으로 정보를 저장하여, 사용자 상태를 유지하는 방법이다.
세션 동작 방식
- 클라이언트가 서버에 접속 시 세션 ID를 발급 받음
- 클라이언트는 세션 ID에 대해 쿠키를 사용해서 저장하고 가지고 있음
- 클라리언트는 서버에 요청할 때, 이 쿠키의 세션 ID를 같이 서버에 전달해서 요청
- 서버는 세션 ID를 전달 받아서 별다른 작업없이 세션 ID로 세션에 있는 클라언트 정보를 가져와서 사용
- 클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답
📒세션을 이용한 로그인
서블릿이 제공하는 세션 기능 사용하기
- 서블릿은 세션을 위해 HttpSession이라는 기능을 제공해준다.
- 서블릿을 통해 HttpSession을 생성하면 쿠키 이름이 JSESSIONID 이고, 값은 추정 불가능한 랜덤 값이다.
- Cookie: JSESSIONID=5B78E23B513F50164D6FDD8C97B0AD05
- 아래 코드는 세션을 생성하고, 세션에 정보를 보관하는 기능이다.
//세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
HttpSession session = request.getSession();
//세션에 로그인 회원 정보 보관
session.setAttribute("loginMember", loginMember);
- request.getSession() 에 인자로 true, false를 줄 수 있다. default = true
- true : 세션이 있으면 기존 세션 반환, 없으면 새로 생성 후 반환
- false : 세션이 있으면 기존 세션 반환 없으면 NULL 반환
📌이해 하기
- 세션 생성
- HttpSession session = request.getSession();
- 세션은 서버 측에 저장되며, 서버는 해당 세션을 식별할 수 있는 고유한 세션 ID를 생성합니다.
- 세션에 데이터 저장
- session.setAttribute("loginMember", loginMember);
- 이 코드는 loginMember 객체를 세션에 저장합니다. 이 데이터는 서버에 저장되며, 세션 ID를 통해 나중에 참조할 수 있습니다.
- 세션 ID 쿠키에 저장
- 서버는 생성된 세션 ID를 클라이언트에 쿠키 형태로 전달합니다. 이 쿠키는 기본적으로 JSESSIONID라는 이름으로 저장됩니다.
- 예를 들어, 클라이언트의 브라우저에 JSESSIONID=xyz12345와 같은 쿠키가 저장됩니다.
- 후속 요청에서 세션 사용
- 사용자가 같은 웹사이트에 다시 요청을 보낼 때, 브라우저는 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 |