쿠키를 통해 로그인 상태를 유지하면 심각한 보안 문제가 발생하기 때문에 session을 이용해서 로그인 처리를 할 수 있다.
서버가 클라이언트에 seesionID만 쿠키에 담아 전달하고, 서버에서는 sessionID에 해당하는 정보들을 저장한다.
- 쿠키 값 변조 -> 예측 불가능한 복잡한 세션ID 사용
- 쿠키에 보관하는 정보 탈취 : 세션ID가 노출되어도 여기에는 중요한 정보가 없음
- 쿠키 탈취 후 사용 : 서버에서 세션의 만료시간을 짧게 유지
서블릿에서 세션을 제공해 주지만, 강의에서 세션을 한번 직접 만들어 보았다.
UUID를 통해 중복될 가능성이 없는 ID를 생성한 후 Cookie의 sessionID로 설정한다.
public void createSession(Object value, HttpServletResponse response) {
// 세션 id를 생성하고, 값을 세션에 저장
String sessionId = UUID.randomUUID().toString();
sessionStore.put(sessionId, value);
//쿠키 생성
Cookie mySessionCooke = new Cookie(SESSION_COOKIE_NAME, sessionId);
response.addCookie(mySessionCooke);
}
만약 쿠키의 값이 없다면 null을 반환하고, 쿠키에 값이 있다면 해당하는 쿠키의 값을 조회해 반환한다.
public Object getSession(HttpServletRequest request) {
Cookie sessionCookie = findCookie(request, SESSION_COOKIE_NAME);
if (sessionCookie == null) {
return null;
}
return sessionStore.get(sessionCookie.getValue());
}
public Cookie findCookie(HttpServletRequest request, String cookieName) {
if (request.getCookies() == null) {
return null;
}
return Arrays.stream(request.getCookies())
.filter(cookie -> cookie.getName().equals(cookieName))
.findAny()
.orElse(null);
}
쿠키를 찾아 삭제한다.
public void expire(HttpServletRequest request) {
Cookie sessionCookie = findCookie(request, SESSION_COOKIE_NAME);
if (sessionCookie != null) {
sessionStore.remove(sessionCookie.getValue());
}
}
TestCode에서 HttpServletResponse를 사용할 수 없으므로 MockHttpServletResponse를 사용하면 된다고 한다. 이에 대해서는 나중에 더 자세히 알아보겠다.
class SessionManagerTest {
SessionManager sessionManager = new SessionManager();
@Test
void sessionTest() {
//세션 생성
MockHttpServletResponse response = new MockHttpServletResponse();
Member member = new Member();
sessionManager.createSession(member, response);
//요청에 응답 쿠키 저장
MockHttpServletRequest request = new MockHttpServletRequest();
request.setCookies(response.getCookies());
//세션 조회
Object result = sessionManager.getSession(request);
Assertions.assertThat(result).isEqualTo(member);
//세션 만료
sessionManager.expire(request);
Object expired = sessionManager.getSession(request);
Assertions.assertThat(expired).isEqualTo(null);
}
}
public HttpSession getSession(boolean create);
request.getSession(true) => Default
request.getSession(false)
public void setAttribute(String name, Object value);
public void invalidate();
@SessiongAttribute 어노테이션을 이용해 세션을 편리하게 사용할 수 있다.
@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";
}
브라우저에서 쿠키를 지원하지 않을 수 있기 때문에 다음과 같이 URL에 jsession을 포함해 지원한다.
URL 전달 방식을 끄고 항상 쿠키를 통해서만 세션을 유지하고 싶다면
application.properties에 server.servlet.session.tracking-modes=cookie 옵션을 넣어주면 된다.