이런 방식을 세션이라고 한다.
Cookie: mySessionId=zz0101xx-bab9-4b92-9b32-dadb280f4b61
중요한 포인트는 회원 정보가 전혀 클라이언트에 전달되지 않는다는 것이다. 오직 추정 불가한 세션 ID만 쿠키로 넘겨준다.
보안 문제를 해결하기 위해, 세션을 이용해 서버에서 중요 정보를 관리한다.
@Component
public class SessionManager {
private static final String SESSION_COOKIE_NAME = "mySessionId";
private Map<String, Object> sessionStore = new ConcurrentHashMap<>();
public void createSession(Object value, HttpServletResponse response) {
// 세션 ID를 생성한 뒤 값을 세션에 저장한다.
String sessionId = UUID.randomUUID().toString();
sessionStore.put(sessionId, value);
// 쿠키를 생성한다.
Cookie mySessionCookie = new Cookie(SESSION_COOKIE_NAME, sessionId);
response.addCookie(mySessionCookie);
}
public Object getSession(HttpServletRequest request) {
Cookie sessionCookie = findCookie(request, SESSION_COOKIE_NAME);
if (sessionCookie == null) {
return null;
}
return sessionStore.get(sessionCookie.getValue());
}
public void expire(HttpServletRequest request) {
Cookie sessionCookie = findCookie(request, SESSION_COOKIE_NAME);
if (sessionCookie != null) {
sessionStore.remove(sessionCookie.getValue());
}
}
private Cookie findCookie(HttpServletRequest request, String cookieName) {
if (request.getCookies() == null) {
return null;
}
return Arrays.stream(request.getCookies())
.filter(c -> c.getName().equals(cookieName))
.findAny()
.orElse(null);
}
}
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);
assertThat(result).isEqualTo(member);
// 세션 만료
sessionManager.expire(request);
Object expired = sessionManager.getSession(request);
assertThat(expired).isEqualTo(null);
}
}
세션을 관리하는 빈을 만든다.
@Slf4j
@Controller
@RequiredArgsConstructor
public class LoginController {
private final SessionManager sessionManager;
@PostMapping("/login")
public String loginV2(@Valid @ModelAttribute LoginForm form, BindingResult bindingResult, HttpServletResponse response) {
...
sessionManager.createSession(loginMember, response);
return "redirect:/";
}
@PostMapping("/logout")
public String logoutV2(HttpServletRequest request) {
sessionManager.expire(request);
return "redirect:/";
}
}
@Slf4j
@Controller
@RequiredArgsConstructor
public class HomeController {
private final SessionManager sessionManager;
@GetMapping("/")
public String homeLoginV2(HttpServletRequest request, Model model) {
// 세션 저장소에서 정보를 가져온다.
Member member = (Member) sessionManager.getSession(request);
if (member == null) {
return "home";
}
model.addAttribute("member", member);
return "loginHome";
}
}
수동으로 쿠키를 생성하고 만료시키던 로직 대신 sessionManager를 주입받아 사용한다.
쿠키 값이 mySessionId로 바뀌었다.