이전에 다른 과제에서 jwt 토큰으로 로그인을 구현해 본 적이 있습니다. -> 원티드 프리온보딩 지원 과제(github 링크)
이 때에는 단순히 토큰 기반 로그인이 많이 사용된다하여 구현했었는데요.
사용한 이후에 해당 기술에 대해 더 찾아보니 아래와 같은 이점이 있었습니다.
1. 토큰 방식은 서버가 클라이언트의 상태를 가지지 않는 stateless한 방식
2. stateless하기 때문에 언제든 token만 있으면 회원 검증가능
3. session을 사용하지 않는 앱 쪽에서도 로그인 기능 확장이 가능(전통적인 웹에선 session 기반으로 인증 값을 가지고 있어야 함)
이전에는 이러한 이해가 부족한 채로 구현했었기 때문에 로그인 시 간단히 access token만 발급하는 방식이었습니다.
그렇지만 네이버나 카카오 로그인 api만 봐도 token 기반 인증 방식은 access token과 refresh token 두가지를 발급하여 사용하는 것을 알 수있었습니다.
jwt 인증 로직 절차
(a.t = access token, r.t = refresh token)
큰 틀에서 토큰은 DB와의 검증의 책임을 가지고 있고 각각의 토큰은 다른 성격을 가지고 있습니다.
- Access token : 회원을 직접적으로 검증하는 토큰
- Refresh token : access token 재발급과 관련된 토큰
각 역할에 따라 다른 접근 방식을 선택해야 한다고 생각했고, 회원정보에 접근할 수 있기 때문에 보안을 위한 저장, 만료시간에 대한 고려가 필요했습니다. 토큰을 저장하는 방법은 아래와 같이 몇가지 방법이 있었습니다.
- 로컬 스토리지 혹은 세션 스토리지
- 쿠키(Http only)
- DB(Redis)
1. access token
먼저 access token의 경우 서버사이드에선 어디에도 저장하지 않고 클라이언트쪽으로 전달만 했습니다.
프론트 영역은 SPA(Single Page Application)로 구현될 가능성이 높기 때문에 이를 활용하여 저장할 수 있고, 이 부분은 추후 논의 후 결정하면 되는 부분이라 생각했기 때문입니다.
아래와 같이 dto를 전달하며 만료시간을 같이 보내주어서 프론트에서 별도로 시간에 대해 고려하지 않고 refresh token을 가지고 토큰을 재발급을 할 수 있도록 했습니다.
유저 접근과 관련된 토큰이기 때문에 유효시간은 짧은시간이라고 판단되는 30분으로 두었습니다.
2. refresh token
refresh token은 역할을 따져보았을 때 직접적으로 유저 검증이 아니라 access token의 무력화와 관련이 있습니다.
토큰 발급에만 영향을 주는 토큰이기 때문에 이것을 어딘가에 저장해야 하는데, DB에 저장하는 것이 낫겠다고 생각이 들었습니다.
로컬이나 세션 스토리지에 저장하면 XSS 공격으로 토큰이 탈취될 가능성이 있고, 쿠키에 저장하면 CSRF 공격을 받을 가능성이 있기 때문입니다.
기존 DB에 Member 와 연관없는 값이 같이 저장하는 것은 이상하다고 느껴 단순하면서 유효시간을 설정할 수 있는 DB를 찾아보았고, redis를 사용하는 것이 맞을 것 같다 생각이 들어 사용하였습니다.
redis의 사용의 장점은 아래와 같습니다.
1. 유효기간(TTL)을 설정할 수 있음.
2. in-memory DB 형태라 속도가 빠름.
3. key-value 형태의 저장으로 단순함.
물론 refresh token 자체에도 유효시간이 있고 이 시간이 지나면 유효하지 않은 값이 됩니다. 그렇지만 DB에 관리가 불필요한 값들을 쌓아가는 것은 자원 낭비라고 생각합니다. 유효기간을 정해 필요없어진 값들은 지워버릴 수 있는 이점이 가장 좋은 부분이라고 생각합니다.
별도의 DB에 refresh token을 저장했으니 유저가 정상적으로 로그아웃 한 경우 refresh token을 삭제하는 기능도 구현했습니다.
시간은 짧지도 길지도 않은 3일로 두어 3일 내에는 재로그인 없이 이용 가능하도록 했습니다.
정리
초반에는 spring security에 대하여 이해하지 못한 부분들이 있어 SecurityContext 에서 값을 가져오는데 애를 먹었습니다.
이후 공부를 하며 나름의 근거를 두고 기능들을 구현해 왔습니다.
완벽한 보안은 어렵겠지만, 가능한 영역들을 가지치기를 해 나가며 확률을 줄여나가고 또 실제로 서비스를 한다면 상황에 따른 대처를 하는 것이 보안에 대한 좋은 방향이 아닐까 싶습니다.
참고자료
https://docs.spring.io/spring-security/site/docs/5.3.x/reference/html5/#csrf-when
https://velog.io/@kmlee95/JWT%EC%9D%98-%EC%95%88%EC%A0%84%ED%95%9C-%EC%A0%80%EC%9E%A5%EC%86%8C
https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/#basics-login
등등..
'성장기록 > 개인프로젝트' 카테고리의 다른 글
sequel pro - caching_sha2_password 오류 (0) | 2024.08.03 |
---|---|
기존 개인 프로젝트 개선 계획 (0) | 2024.07.31 |
트러블 슈팅 - 동시 구매 시 재고관리에 대한 고민 2. 동시 구매시 재고관리 접근 (0) | 2024.03.11 |
트러블 슈팅 - 동시 구매 시 재고관리에 대한 고민 1. 재고 관리 프로세스 (0) | 2024.03.08 |
트러블 슈팅 - DB 연관관계에서의 N+1 문제 (0) | 2024.03.04 |
남에게 설명할 때 비로소 자신의 지식이 된다.
포스팅이 도움되셨다면 하트❤️ 또는 구독👍🏻 부탁드립니다!! 잘못된 정보가 있다면 댓글로 알려주세요.