[Spring MVC] [2] 6. 로그인 처리1 - 쿠키, 세션

윤경·2021년 9월 23일
0

Spring MVC

목록 보기
20/26
post-thumbnail

[1] 로그인 요구사항

홈 화면 (로그인 전)

  • 회원가입
  • 로그인

홈 화면 (로그인 후)

  • "**님 환영합니다."
  • 상품 관리
  • 로그아웃

보안 요구사항

  • 로그인 사용자만 상품에 접근, 관리
  • 로그인 안 한 사용자가 상품 관리에 접근하면 로그인 화면으로 이동

회원 가입, 상품 관리


[2] 프로젝트 생성

도메인이 가장 중요
도메인 = 화면, UI, 기술 인프라 등등 영역은 제외한 시스템이 구현해야 하는 핵심 비즈니스 영역을 말함

향후 web을 다른 기술로 바꿔도 도메인은 유지되어야 함

이는, web은 domain을 의존하지만 domain은 web을 의존 X
단방향으로 의지해야 한다.


[3] 홈 화면


[4] 회원 가입

Optional: 껍데기 빈 통이라고 생각하기
(람다식에서).filter: 이 조건에 만족하는 것만 다음 단계로 넘어갈 수 있음

그리고 서버를 재실행 시킬 때마다 회원을 생성하기 번거로우니 테스트용 회원 데이터를 TestDataInit에 생성했다.


[5] 로그인 기능

아직까지는 로그인에 성공하면 이렇게 회원 가입과 로그인이 뜨는 홈으로 이동. (추후 상품 관련 화면으로 전환할 것)

bindingResult.reject()를 사용해 글로벌 오류 (ObjectError)를 생성


[6] 로그인 처리하기 - 쿠키🍪 사용

  • 영속 쿠키: 만료 날짜를 입력하면 그 날짜까지 살아있음
  • 세션 쿠키: 만료 날짜를 생략하면 브라우저 종료시까지 살아있음

test로그인 개발자도구로 쿠키 확인

쿠키 삭제
로그아웃시 응답 쿠키를 생성. 이때 Max-Age=0으로 해당 쿠키는 즉시 종료.


[7] 쿠키🍪와 보안 문제

1. 쿠키 값은 임의로 변경 가능
클라이언트가 쿠키를 강제로 변경할 수 있어 다른 사용자 정보를 훔쳐볼 수 있다.

2. 쿠키에 보관된 정보는 훔칠 수 있다.
회원 이름만 있는 것이 아니라 신용카드 정보라도 있다면?

정보가 웹 브라우저에도 보관되고 네트워크 요청마다 계속 클라이언트 서버로 전달되는데 이때 내 PC가 감염되어 털릴 수도 있고 네트워크 전송 구간에서도 털릴 수 있다.

3. 해커가 쿠키를 한 번 훔쳐가서 평생 이용
해커가 내 신용카드 정보를 훔쳐가 악의적 요청을 무한대로 요청할 수 있다.

그래서,

  • 쿠키에 중요한 값을 노출시키지 않으며 예측 불가능한 임의의 토큰을 노출시킨다. 서버에서 이를 매핑해 인식하고 서버에서 토큰을 관리한다.
  • 토큰은 해커가 임의의 값을 넣어도 찾을 수 없도록 예상 불가능해야 한다.
  • 해커가 평생 토큰을 이용하는 것을 막기 위해 토큰 만료 시간을 짧게 잡는다. 또는 해킹이 의심되면 서버에서 해당 토큰을 강제로 제거한다.

[8] 로그인 처리하기 - 세션 동작 방식

절대 추정할 수 없는 값으로 세션 ID를 생성한다. UUID를 추정해 맞을 확률은 로또💰 맞을 확률보다 낮다^^

서버가 클라이언트에게 mySessionId라는 이름으로 세션ID만 쿠키에 담아 전달. 클라이언트는 쿠키 저장소에 쿠키 저장.

회원과 관련된 정보는 클라이언트에게 전달 X. 서버가 관리하기 때문.

  • 쿠키 값 변조 ➡️ 예상 불가능한 복잡한 세션 ID
  • 쿠키에 보관된 정보 털릴 수도? ➡️ 세션 ID가 털려도 중요한 정보가 없으므로 상관 X
  • 쿠키 탈취 후 사용 ➡️ 세션 만료 시간이 짧아 시간이 지나면 이용할 수 없음. 또한 해킹이 의심되면 서버에서 세션 종료해버림.

[9] 로그인 처리하기 - 세션 직접 만들기

option + command + c: 상수 만드는 단축키

📌 세션 관리가 제공해야 할 기능
1. 세션 생성 (sessionId)
2. 세션 조회
3. 세션 만료

@Component: 스프링 빈으로 자동 등록
ConcurrentHashMap: 해시맵은 동시 요청에 안전 X. 동시 요청에 안전한 맵

HttpServletRequest, HttpservletResponse 객체를 직접 사용할 수 없어 비슷한 역할을 해주는 가짜 MockHttpServletRequest, MockHttpServletResponse를 사용


[10] 로그인 처리하기 - 직접 만든 세션 적용

sessionManager.createSession(loginMember, response);: 로그인 성공시 세션 등록. 세션에 loginMember 저장, 쿠키도 함께 발행

결국 세션은 서버에서 데이터를 유지하는 방법일 뿐.
지금까진 직접 세션을 만들었고 서블릿이 세션을 공식 지원하므로 제공된 세션을 쓰자.


[11] 로그인 처리하기 - 서블릿 HTTP 세션1

📌 HttpSession
SessionManager과 같은 방식으로 동작
서블릿을 통해 HttpSession을 생성하면 추정 불가능한 랜덤 값을 가진 쿠키를 생성한다.

request.getSession(true): 세션 생성
이때 true/false는 create에 대한 값이다.
둘 다 세션이 있으면 그 세션을 반환하고, true일 때 세션이 없으면 생성해 반환하는 반면 false일 때는 세션이 없다면 새로운 세션을 생성하지 않고 null을 반환한다.
(default값은 true)

session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);: 세션에 로그인 회원 정보 보관. 하나의 세션에 여러 값을 저장할 수 있다.


[12] 로그인 처리하기 - 서블릿 HTTP 세션2

@SessionAttribute: 스프링이 세션을 편리하게 이용할 수 있도록 지원

ex. 로그인 된 사용자 찾기: @SessionAttribute(name = "loginMember", required = false) Member loginMember
(세션을 찾고, 세션에 들어있는 데이터를 찾는 번거로운 과정을 삭제)

  http://localhost:8080/;jsessionid=F59911518B921DF62D09F0DF8F83F872

➡️ 웹 브라우저가 쿠키를 지원하지 않을 경우 URL로 대신 세션을 유지하는 방법

이렇게 세션을 유지하려면 URL에 이 값(jsessionid)을 계속 포함해 전달해야 한다. 타임리프 같은 템플릿은 엔진을 통해 링크를 걸면 jsessionid를 URL에 자동으로 포함해준다.

서버 입장에서는 최초에는 웹 브라우저의 쿠키 지원 여부를 알 수 없으므로 쿠키 값도 전달, jsessionid도 전달한다.

📌 url이 아닌 쿠키로만 세션을 유지하고 싶다면 application.properties에 아래의 코드 추가하기

  server.servlet.session.tracking-modes=cookie

[13] 세션 정보와 타임아웃 설정

sessionId: 세션 id, JSESSIONID의 값(추정 불가능한 랜덤 값)

maxInactiveInterval: 세션 유효 시간

creationTime: 세션 생성 일시

lastAccessedTime: 세션과 연결된 사용자가 최근 서버 접속 시간. 클라이언트에서 서버로 sessionId 즉, jsessionid를 요청한 경우 갱신

isNew: 새로 생성된 세션인지 과거에 만들어져 클라이언트에서 서버로 sessionId를 요청해 조회된 세션인지에 대한 여부

📌 세션 타임아웃 설정
사용자가 로그아웃을 호출해 session.invalidate()를 호출한 경우 바로 삭제.
그런데 대부분은 로그아웃을 하지 않고 웹 브라우저를 종료한다. 이때 HTTP는 비연결성이므로 서버 입장에서 사용자가 웹 브라우저를 종료했는지 아닌지 인식할 수 없다.
따라서, 서버에서 세션 데이터를 언제 삭제해야 하는지 판단하기 어렵다.

이렇게 되면 쿠키를 털렸을 때 시간이 지나도 피해를 입을 수 있으며 세션이 계속 쌓여 방대해질 수 있다. (세션은 기본적으로 메모리에 생성)

그래서 이에 대한 대안은 사용자가 서버에 요청한 가장 최근 시간을 기준으로 30분정도 세션을 유지시키는 것이다.

세션의 타임아웃 시간은 해당 세션과 관련된 jsessionid를 전달하는 http 요청이 있으면 현재 시간으로 다시 초기화된다. 이렇게 초기화 되면 세션 타임아웃으로 설정한 시간동안 세션을 추가로 사용할 수 있다.
LastAccessedTime 이후 timeout 시간이 지마녀 WAS가 내부에서 해당 세션 제거.

session.getLastAccessedTime(): 최근 세션 접근 시간


profile
개발 바보 이사 중

0개의 댓글