스프링 MVC 2편 - 백엔드 웹 개발 활용 기술을 공부하고 정리하는 포스트입니다.


로그인 처리

  • 로그인의 핵심 비즈니스 로직
    • 회원을 조회한 다음 파라미터로 넘어온 password와 비교
      → 같으면 회원을 반환, 다르면 null을 반환

로그인 처리하기 - 쿠키 사용

로그인 상태 유지하기

  • 쿼리 파라미터를 계속 유지하면서 보내는 것은 매우 어렵고 번거로운 작업
  • 서버에서 로그인에 성공하면 HTTP 응답에 쿠키를 담아서 브라우저에 전달 → 브라우저가 해당 쿠키를 지속해서 전송

  • 쿠키 생성


  • 클라이언트 쿠키 전달



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

세션 쿠키 생성

Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
response.addCookie(idCookie);
  • 로그인 성공 시 쿠키를 생성하고 HttpServletResponse에 담음

로그아웃 기능

  • 로그아웃 방법
    • 세션 쿠키이르모 웹 브라우저 종료 시
    • 서버에서 해당 쿠키의 종료 날짜를 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);
}

쿠키와 보안 문제

보안 문제

  • 쿠키 값은 임의로 변경 가능
    • 클라이언트가 쿠키를 강제로 변경하면 다른 사용자가 됨
    • 웹 브라우저에서 개발자모드 → Application → Cookie 변경
  • 쿠키에 보관된 정보는 훔쳐갈 수 있음
    • 쿠키에 개인정보나 신용카드 정보가 있다면?
      → 이 정보가 웹 브라우저에도 보관되고, 네트워크 요청마다 계속 클라이언트에서 서버로 전다룀
    • 쿠키의 정보가 로컬 PC에서 털릴 수도 있고, 네트워크 전송 구간에서 털릴 수도 있음
  • 쿠키를 한 번 훔쳐가면 평생 사용 가능
    • 해커가 쿠키를 훔쳐가서 그 쿠키로 악의적인 요청을 계속 시도할 수 있음

대안

  • 쿠키에 중요한 값을 노출하지 않아야 함
    • 사용자 별로 예측 불가능한 임의의 토큰(랜덤 값)을 출력
    • 서버에서 토큰과 사용자 id를 매핑해서 인식하도록 구성 → 서버에서 토큰을 관리
  • 토큰은 임의의 값을 넣어도 찾을 수 없도록 구성해야 함
  • 서버에서 해당 토큰의 만료시간을 짧게(약 30분) 유지. 해킹이 의심되는 경우 해당 토큰을 강제로 제거

로그인 처리하기 - 세션 동작 방식

  • 세션은 쿠키를 사용하지만, 서버에서 데이터를 유지하는 방법

  • 세션 생성
    • sessionId 생성(임의의 추정 불가능한 랜덤 값)
    • 세션 저장소에 sessionId와 보관할 값 저장
    • sessionId로 응답 쿠키를 생성해서 클라이언트에 전달
  • 세션 조회
    • 클라이언트가 요청한 sessionId 쿠키의 값으로 저장소에 보관한 값 조회
  • 세션 만료
    • 클라이언트가 요청한 sessionId 쿠키의 값으로 저장소에 보관한 sessionId와 값 제거

로그인 처리하기 - 서블릿 HTTP 세션

  • 서블릿에서 세션을 위해 HttpSession이라는 기능을 제공
    • 서블릿을 통해 HttpSession을 생성하면 JSESSIONID라는 추정 불가능한 랜덤 값을 가지는 쿠키를 생성함

세션 생성과 조회

  • 세션을 생성할 때 request.getSession(true)를 사용하면 됨
public HttpSession getSession(boolean create);
  • 세션의 create 옵션
    • request.getSession(true)
      • 세션이 있으면 기존 세션을 반환
      • 세션이 없으면 새로운 세션을 생성해서 반환
    • request.getSession(false)
      • 세션이 있으면 기존 세션을 반환
      • 세션이 없으면 세션을 생성하지 않고 null을 반환
  • request.getSession() : true와 동일

로그인 회원 정보 보관

  • session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember)
    • 세션에 데이터를 보관하는 방식은 request.setAttribute(...)와 비슷
    • 하나의 세션에 여러 값 저장 가능

@SessionAttribute

  • 스프링에서 지원 → 로그인 된 사용자를 찾을 때 사용
    • 세션을 생성하지 않음

TrackingModes

  • 웹 브라우저가 쿠키를 지원하지 않을 때 쿠키 대신 URL을 통해서 세션을 유지하는 방법
    • URL에 값을 계속 포함해서 전달
  • 타임리프 같은 템플릿은 엔진을 통해서 링크를 걸면 jsessionid를 URL에 자동으로 포함
  • 서버 입장에서 웹 브라우저가 쿠키를 지원하는지 하지 않는지 최초에는 판단하지 못하므로, 쿠키 값도 전달하고, URL에 jsessionid도 함께 전달

  • URL 전달 방식을 끄고 항상 쿠키를 통해서만 세션을 유지하고 싶으면 다음 옵션을 사용
server.servlet.session.tracking-modes=cookie

세션 정보와 타임아웃 설정

  • sessionId : 세션Id, JSESSIONID의 값이다. 예) 34B14F008AA3527C9F8ED620EFD7A4E1
  • maxInactiveInterval : 세션의 유효 시간. 예) 1800초
  • creationTime : 세션 생성일시
  • lastAccessedTime : 세션과 연결된 사용자가 최근에 서버에 접근한 시간, 클라이언트에서 서버로 sessionId(JSESSIONID)를 요청한 경우에 갱신
  • isNew : 새로 생성된 세션인지, 아니면 이미 과거에 만들어졌고, 클라이언트에서 서버로 sessionId(JSESSIONID)를 요청해서 조회된 세션인지 확인

세션 타임아웃 설정

  • 세션은 사용자가 로그아웃을 직접 호출해서 session.invalidate()가 호출 되는 경우 삭제됨
    • 대부분의 사용자는 브라우저를 종료
    • 서버 입장에서는 인식할 수 없음
  • 남아있는 세션을 무한정 보관 시
    • 세션과 관련된 쿠키(JSESSIONID)를 탈취 당했을 경우 오랜 시간이 지나도 해당 쿠키로 악의적인 요청을 할 수 있음
    • 세션은 기본적으로 메모리에 생성하기 때문에 꼭 필요한 경우만 생성해서 사용하는게 좋음

세션 종료 시점

  • 세션 생성 시점으로부터 약 30분
  • 더 좋은 대안은 생성 시점이 아닌 사용자가 서버에 최근 요청한 시간을 기준으로 약 30분 간 유지

스프링 부트로 글로벌 설정

  • 기본 설정 : 60초
server.servlet.session.timeout=60
  • 특정 세션 단위로 시간 설정
session.setMaxInactiveInterval(1800); //1800초

세션 타임아웃 발생

  • 세션의 타임아웃 시간은 해당 세션과 관련된 JSESSIONID를 전달하는 HTTP 요청이 있으면 현재 시간으로 다시 초기화 됨
    • session.getLastAccessedTime() : 최근 세션 접근 시간
    • LastAccessedTime 이후로 timeout 시간이 지나면, WAS가 내부에서 해당 세션을 제거