[본 포스팅은 스프링 MVC 2편 백엔드 웹 개발 핵심 기술 편을 기반으로 작성하였습니다.]
웹사이트에 로그인을 하게 되면 로그인이 유지된 상태로 서비스를 이용할 수 있어야 한다.
그리고 이용할 수 있게 해주는 것이 쿠키이다.
하지만 쿠키를 사용해서 로그인 Id를 전달하는 쿠키에는 보안 문제가 있다.
- 쿠키 값은 임의로 변경할 수 있다.
- 쿠키에 보관된 정보는 훔쳐갈 수 있다.
- 해커가 쿠키를 한번 훔쳐가면 평생 사용할 수 있다.
그래서 이러한 보안 문제를 해결하기 위해
- 쿠키에 중요한 값을 노출하지 않고 예측 불가능한 토큰을 노출하고, 서버에서 토큰과 사용자 Id를 매핑한다.
- 해커가 토큰을 털어가도 시간이 지나면 사용할 수 없도록 토큰 만료시간을 짧게 지정한다.
따라서 세션 사용을 해야 한다.
세션이란?
└ 서버에 중요한 정보를 보관하고 연결을 유지하는 방법
세션에서 id를 생성해서 클라이언트 쿠키에 전달하는 모습은 다음과 같다.
위의 그림을 순서대로 말하자면
- 사용자가 로그인 요청을 한다.
- 서버에서는 로그인 요청 정보를 확인하고 세션 Id를 생성하고 세션 저장소에 저장한다.
- 사용자에게 쿠키 정보를 보낸다.
- 사용자의 웹브라우저의 쿠키 저장소에 쿠키가 저장된다.
기존의 방법은 세션을 생성, 조회, 만료를 직접 만들었지만 서블릿 HTTP 세션을 활용하면 간단하게 사용할 수 있다.
서블릿 HTTP 세션 - 로그인
@PostMapping("/login")
public String loginV3(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, 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:/";
}
HttpSession을 통해 세션을 만들게 되는데 getSession();의 매개변수는 기본으로 true이다.
true는 세션이 없으면 세션을 생성해주고
false는 세션이 없으면 세션을 생성해주지 않는다.(로그인을 하지 않은 상태에서 홈페이지에 들어갈 때나 로그아웃 등)
그리고 session.setAttribute()의 매개변수 첫 번째는 속성 값의 이름, 두번째는 유저의 Member 데이터를 넣어줬다.
그래서 확인해보면 위 사진처럼 저장이 된다.
서블릿 HTTP 세션 - 로그아웃
@PostMapping("/logout")
public String logoutV3(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/";
}
로그아웃은 세션을 받아내서 세션이 있으면 invalidate 시킨다.
서블릿 HTTP 세션 - 기본 홈
@GetMapping("/")
public String homeLoginV3Spring(@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) {
//세션에 회원 데이터가 없으면 home
if (loginMember == null) {
return "home";
}
//세션이 유지되면 로그인으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
- @SessionAttribute를 통하여 위에 로그인 시 설정해두었던 session.setAttribute()의 키값을 여기서 name 값에 넣어준다.
- 쿠키가 null값일 수도 있기 때문에 required는 false로 해두었다.
- 뒤에 Member loginMember에 매핑을 하여 값을 넣어주었다.
하지만 사용자들은 로그인을 하고 로그아웃을 하면서 종료하진 않는다.
따라서 세션은 만료기간이 필요한데 기본적으로 다음과 같이 설정된다.
위에서부터
- 세션 정보
session.getAttributeNames().asIterator().forEachRemaining(name -> log.info("session name={}, value={}", name, session.getAttribute(name)));
- 세션 ID
log.info("sessionId={}", session.getId());
- 만료 시간(마지막 활동 시간을 기준으로 만료)
log.info("getMaxInactiveInterval={}", session.getMaxInactiveInterval());
- 생성 시간
log.info("creationTime={}", new Date(session.getCreationTime()));
- 마지막 활동 시간
log.info("lastAccessedTime={}", new Date(session.getLastAccessedTime()));
- 새로 생성된 세션인지 아니면 이미 과거에 만들어졌고 클라이언트에서 서버로 세션 ID를 요청해서 조회된 세션인지
log.info("isNew={}", session.isNew());
마무리
기존의 방식처럼 세션 관련 코드를 짜지 않아도 서블릿 HTTP 세션을 사용하면 간단하게 코드를 짤 수 있다.
추가적으로 처음 로그인 시 주소창에 토큰 아이디가 포함되는 것을 확인할 수 있는데 주소만 보이게 하려면
application.properties에
server.servlet.session.tracking-modes=cookie
추가해주면 된다.
'JAVA > Spring' 카테고리의 다른 글
[Spring/Thymeleaf] 타임리프로 팝업창 띄워보기 (feat. div 수직 중앙 정렬) (0) | 2022.10.14 |
---|---|
[Spring] 스프링 인터셉터란? / 사용해보기 (feat. 비로그인 접근 막기) (2) | 2022.09.21 |
[Spring] Bean Validation의 한계, 해결하기 위한 방법 두 가지(groups, Form 나누기) (0) | 2022.09.19 |
[Spring] Bean Validation이란? / 사용해보기 (0) | 2022.09.19 |
[Spring] MVC 패턴의 개요 (feat. 사용 예시) (0) | 2022.09.07 |