springMvc2_Login
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - (로그인 처리)
쿠키는 브라우저에 담겨, 모든 요청이 있을 때마다 서버도 전달된다.
쿠카는 영속쿠키와 세션쿠키가 있다
- 영속 쿠키 : 만료 날짜를 입력하면 해당 날짜까지 유지된다.
- 세션 쿠키 : 만료 날짜를 생략하면 브라우저 종료시 까지만 유지된다.
브라우저 종료시 로그아웃이 되길 기대하므로, 우리에게 필요한 것은 세션 쿠키이다.?? - 이상하네 우리는 요청 할 때마다 연장되는거였으니까 세션쿠키가 아니라 영속쿠키를 한거아냐???????
그렇다면 로그인과 로그아웃 처리를 Cookie를 통해 해보자
LoginController.java
// 로그인
Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
httpResponse.addCookie(idCookie);
// 로그아웃
@PostMapping("/logout")
public String logout(HttpServletResponse response){
expireCookie(response, "memberId");
return "redirect:/";
}
private void expireCookie(HttpServletResponse response, String cookieName) {
Cookie cookie = new Cookie("memberId", null);
cookie.setMaxAge(0); // 해당 쿠키의 종료날짜를 0으로 지정한다.
response.addCookie(cookie);
}
하지만 저렇게 쿠키값으로만 로그인 로그아웃을 한다면,
쿠키는 값을 임의로 변경할 수 있기때문에 다른 사용자로도 변신할 수 있다.
대안 [세션] 이용하기
- 쿠키에 중요한 값을 노출시키지 않고 예측 불가능한 임의의 토큰값을 노출한다.
- 서버에서 토큰값을 사용자id와 매핑해서 인식한다.
단, 토큰은 만료시간을 짧게 유지한다(30분) - 토큰은 해당 서버에서 관리한다.
세션을 어떻게 할 것인지 전체 개념을 이해해보자
- 사용자가 loginId, password를 전달하면, 서버는 해당 사용자가 맞는지 확인을 한다.
2. 해당 사용자가 맞을 경우 UUID라는 랜덤값을 생성하여 세션저장소에 담아둔다.
생성된 세션ID와 세션에 보관할 값(Member객체)을 세션 저장소에 보관한다.
- Cookie: mySessionId=zz0101xx-bab9-4b92-9b32-dadb280f4b61
3. sessionId를 담아 쿠키로 전달한다.
- 클라이언트와 서버는 결국 쿠키로 연결이 되어야 한다
여기서 중요한 포인트는 회원과 관련된 정보는 클라이언트에 전달하지 않는것
또한, 추정 불가능한 세션 ID만 쿠키를 통해 클라이언트에 전달한다.
- 로그인 이후
- 클라이언트의 요청 시 항상 sessionId를 전달한다.
- 서버에서는 클라이언트가 전달한 sessionId 쿠키정보로
세션 저장소를 조회해서 로그인 시 보관한 세션 정보를 사용한다.
정리
세션저장소에 sessionId(uuid)를 저장하고 sessionId(uuid)만을 클라이언트 브라우저에 저장할 경우
아래와 같은 보안문제들을 해결할 수 있다.
문제 | 해결 |
---|---|
쿠키 값 변조 가능 | 예상 불가능한 복잡한 세션Id를 사용 |
쿠키를 보관하는 정보는 클라이언트 해킹 시 털릴 가능성이 있다. | 세션Id에는 중요한 정보가 없다. |
쿠키 탈취 후 사용 | 세션 만료시간을 30분 정도로 짧게 유지하고 클라이언트 요청이 있을 때마다 유지시간을 연장한다. 또한, 해킹이 의심되는 경우 세션을 강제로 제거한다. |
세션관리는 크게 3가지로 나뉜다.
- 세션 생성
- sessionId 생성 (임의의 추정 불가능한 랜덤 값)
- 세션 저장소에 sessionId와 보관할 값을 저장
- 세션 조회
- 클라이언트가 요청한 sessionId쿠키의 값으로, 세션저장소에 보관할 값 조회
- 세션 만료
- 클라이언트가 요청한 sessionId쿠키의 값으로, 세션 저장소에 보관한 sessionId 값 제거
Servlet이 제공하는 HttpSession을 사용하여 만들어보자
HttpSession을 사용하면 Cookie이름이 JSEESSIONID으로 값은 불가능한 랜던값이 생성된다.
Cookie: JSESSIONID=5B78E23B513F50164D6FDD8C97B0AD05
1. 세션 생성
HttpSession session = request.getSession(true); // defalt가 true
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
구분 | 세션 O 반환값 | 세션 X 반환값 |
---|---|---|
request.getSession(true) | 기존 세션 | 새로 생성 |
request.getSession(false) | 기존 세션 | Null |
@PostMapping("/login")
public String login(@Valid @ModelAttribute LoginForm loginForm, BindingResult bindingResult, HttpServletRequest request){
if(bindingResult.hasErrors()){
return "login/loginForm";
}
Member loginMember = loginService.login(loginForm.getLoginId(), loginForm.getPassword());
if(loginMember == null){
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
HttpSession session = request.getSession(true); // defalt가 true
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
return "redirect:/";
}
2. Home 진입 시 Session 가져오기
HttpSession session = request.getSession(false);
if(session == null){
return "home";
}
Member member = (Member) session.getAttribute(SessionConst.LOGIN_MEMBER);
위 5줄 짜리 소스를 아래 어노테이션으로 한번에 처리 가능하다
public String homLogin4(
@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member member
, Model model){
@GetMapping("/")
public String homLogin4(
@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member member, Model model){
// // Home화면 진입이기때문에
// // 세션을 생성할 의도가 없기에 getSssion(false)로 지정해줘야한다.
// HttpSession session = request.getSession(false);
// if(session == null){
// return "home";
// }
//
// Member member = (Member) session.getAttribute(SessionConst.LOGIN_MEMBER);
if(member == null){
return "home";
}
if(member == null){
return "home";
}
model.addAttribute("member", member);
return "loginHome";
}
3. 세션 만료
- HttpSession session = request.getSession(false);
- session.invalidate();
@PostMapping("/logout")
public String logout3(HttpServletRequest request){
HttpSession session = request.getSession(false);
if(session != null){
session.invalidate(); // 무효화하다 invalidate
}
return "redirect:/";
}
그런데 URL에 JSESSIONID로 아래와 같이 들어오기때문에
브라우저에서 쿠키를 지원하지 않는 경우로 만들어진 것이다.
Application.properties에 다음 내용을 넣어주자
server.servlet.session.tracking-modes=cookie
- 영속쿠키
- 세션쿠키
- Cookie cookie = new Cookie(“memberId”, String.valueOf(loginMember.getId());
HttpResponse.addCookie(cookie);
- Cookie cookie = new Cookie(“memberId”, null);
cookie.setMaxAge(0);
response.addCookie(cookie);
- 세션
1. 세션 생성
HttpSession session = request.getSession(true); // defalt가 true
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
2. 세션만료
HttpSession session = request.getSession(false);
if(session != null){
session.invalidate(); // 무효화하다 invalidate
}
3. 세션가져오기
HttpSession session = request.getSession(false);
Member member = (Member) session.getAttribute(SessionConst.LOGIN_MEMBER);
===> @SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member member