본 게시물은 스스로의 공부를 위한 글입니다.
잘못된 내용이 있다면 댓글로 알려주세요!
쿠키, 세션의 기본 지식이 있어야 이해하기 쉽습니다!
쿠키만을 이용해 로그인 서비스를 만들자
Cookie 생성자: Cookie(name, value)
. 둘다 String이다.
아이디와 패스워드가 일치한지 확인 후 다음과 같은 코드로 쿠키를 만들고, response에 담아준다.
//쿠키에 시간 정보를 주지 않으면 세션 쿠키(브라우저 종료시 모두 종료)
Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
response.addCookie(idCookie);
@GetMapping("/")
public String homeLogin(@CookieValue(name = "memberId", required = false) Long memberId,
Model model) {
if (memberId == null) return "home";
//로그인
Member loginMember = memberRepository.findById(memberId);
if (loginMember == null) return "home";
model.addAttribute("member", loginMember);
return "loginHome";
}
@cookieValue
를 사용하면 name
으로 설정되어 있는 쿠키를 꺼내 올 수 있다!required=false
를 써줬다.어? 쿠키 값은 String인데, 어떻게 Long 타입으로 받을 수 있지?
- 스프링이 알아서 컨버팅해서 Long으로 바꿔주기 때문에 걱정하지 않아도 된다.
@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);
}
대안점은?
쿠키에 예측 불가능한 임의의 토큰을 노출해야 한다.
토큰의 만료시간을 짧게(예: 30분) 유지하게 한다.
해킹이 의심되는 경우에는 서버에서 해당 토큰을 컨트롤할 수 있어야 한다.
쿠키+세션 조합으로 위 보안 문제를 해결하자!
과정
서블릿은 세션을 위해 HttpSession
이라는 기능을 제공해준다.
서블릿을 통해 HttpSession
을 생성하면 쿠키 이름이 JSESSIONID
이고, 값은 추정 불가능한 랜덤 값으로 세팅된다.
Cookie: JSESSIONID=5B78E23B513F50164D6FDD8C97B0AD05
아래 코드는 세션을 생성하고, 세션에 정보를 보관하는 기능이다.
//세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
HttpSession session = request.getSession();
//세션에 로그인 회원 정보 보관
session.setAttribute('loginMember', loginMember);
request.getSession()
에 인자로 true
, false
를 줄 수 있다.(디폴트=true
)true
: 세션이 있으면 기존 세션 반환, 없으면 새로 생성 후 반환false
: 세션이 있으면 기존 세션 반환, 없으면 null 반환😎 내가 헷갈렸던 부분
다음은 내가 공부하면서 헷갈렸던 부분을 다시 정리한 내용이다.
JSESSIONID
를 사용한다.JSESSIONID
로 구분한다.name
, value
이다.seesion.setAttribute
로 여러개의 row를 넣을 수 있는데, 이때 value
값으론 Object
를 넣을 수 있다.request
는 HttpServletRequest request
이다. (컨트롤러에서 인자로 받으면 된다.)//아이디, 패스워드를 비교 후 올바른 정보면 아래 코드 실행
//세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
HttpSession session = request.getSession();
//세션에 로그인 회원 정보 보관
session.setAttribute("loginMember", loginMember);
HttpSession session = request.getSession(false);
if (session == null) return "home";
Member loginMember = (Member) session.getAttribute("loginMember");
//세션에 회원 데이터가 없으면 home
if (loginMember == null) return "home";
Q) 그럼 접속자가 로그인을 했는지, 안했는지 확인을 하려면 계속 위와 같은 코드를 넣어야 하는거야? 너무 귀찮고, 코드도 길어지는데..
A) 아니! 필터나 인터셉터를 이용하면 쉽게 처리할 수 있지. 이건 다음 게시물에서 알아보자.
//세션을 삭제한다.
HttpSession session = request.getSession(false);
if (session != null) session.invalidate();
기본적으로 세션은 서버의 메모리에 올라간다.
10만명이 접속해 세션을 만든다면? 그만큼의 정보가 메모리에 올가가게 된다!
이를 그나마 해소하기 위한 방법이 2가지 있다.
글로벌 설정
application.properties
에 server.servlet.session.timeout=60
(초단위)특정 세션 단위로 시간 설정
session.setMaxInactiveInterval(1800);
Q) 아니 그러면 30분이 지나면 무조건 다시 로그인해야 되는거 아니야?
정말 편리하게도.. 최근 세션 접근 시간을 기준으로 계속 타임을 초기화 시켜준다.
그니깐.. 아무것도 안하고 가만히 있으면 다시 로그인해야 하지만,
웹을 돌아다니며 세션을 계속 요청하면, 요청할때마다 타임은 초기화 돼서 다시 로그인할 필요가 없다.
아.. 그니깐 사용자가 얼마나 그 페이지에 머물지를 잘 예측해서 너무 짧지도 않고, 너무 길지도 않게 잘 설정해야 하겠구나!
value
값으로 Object
을 넣을 수 있다.Object
가 크면 그만큼 메모리를 차지한다는 이야기이다. (그래서 세션은 사실 비싼 자원이다..)@SessionAttribute
를 사용해보자.@GetMapping("/")
public String homeLoginV3Spring(
@SessionAttribute(name = "loginMember", required = false)
Member loginMember) {
//세션에 회원 데이터가 없으면 로그인 화면으로
if (loginMember == null) return "login";
//회원 정보가 확인되면 홈으로 이동
return "home";
}
로그인을 처음 시도하면 URL이 다음과 같이 jsessionid 를 포함하고 있는 것을 확인할 수 있다.
http://localhost:8080/;jsessionid=F59911518B921DF62D09F0DF8F83F872
이건 웹 프라우저가 쿠키를 지원하지 않을 상황을 대비하여 쿠키 대신 URL을 통해 세션을 유지하는건데... 사실 실제론 잘 사용하지 않는다.
기능을 꺼버리자
application.properties
에 다음 코드 추가server.servlet.session.tracking-modes=cookie
//세션 데이터 출력
session.getAttributeNames().asIterator(). forEachRemaining(name -> log.info("session name={}, value={}",
name, session.getAttribute(name)));
log.info("sessionId={}", session.getId());
log.info("세션의 유효시간(초)={}", session.getMaxInactiveInterval());
log.info("세션 생성일시={}", new Date(session.getCreationTime()));
log.info("최근 서버에 접근한 시간={}", new Date(session.getLastAccessedTime()));
log.info("지금 만들어진 세션인가?={}", session.isNew());
위에서 언급했던것처럼 다음 게시물에선 필터와 인터셉터에 대해 알아보겠다.
2편 보러가기: 필터, 인터셉터
인프런의 '스프링 MVC 2편(김영한)'을 스스로 정리한 글입니다.
자세한 내용은 해당 강의를 참고해주세요.