JWT이용한 인증시 중복로그인 방지 로직 구현

JeongO·2023년 2월 26일
0

당근플래너

목록 보기
1/2

당근플래너 서비스를 배포하고 발견한 문제가 중복로그인 문제였다.

🔴 문제 상황

  • 유저가 브라우저에서 로그인을 한다.
  • 시간이 지난 후 새로 연 브라우저에서 또 로그인을 한다.
  • 다시 시간이 지나고 이전에 로그인을 했던 브라우저에서 요청을 보낸다.
  • 이때 시간이 지나 AccessToken이 만료되었으므로 Refresh Token으로 토큰 재발행 요청을 보낸다.
  • Redis에는 두 번째 로그인했을 때 발급된 새로운 Refresh Token이 저장되어 있으므로 유저가 보낸 Refresh Token과는 일치하지 않게 된다.

문제는 중복로그인이 된 상태로 기능을 이용하는 것도 문제였지만,
이렇게 Refresh Token이 일치하지 않았을 때 유저 입장에서 서비스가 먹통이 된 상태를 마주하게 된다는 것이었다.


프론트엔드 분과 협의하여 예외처리를 보완해 해당 유저를 로그아웃시키고, 먼저 발급된 Access Token을 무효화 시키는 로직을 추가로 구현하여 중복로그인을 방지했다.

🟡 JWT인증의 단점

세션인증방식을 사용할 경우 서버에서 세션ID를 관리하므로 해당 세션ID를 삭제하기만 하면 중복로그인을 방지할 수 있지만 JWT기반 인증을 사용할 경우, Access Token을 서버에서 관리하지 않기 때문에 이미 발급된 토큰을 제어할 수가 없었다.
원래도 JWT의 단점으로 많이 언급되는 것이긴 했다.


어찌되었든, JWT를 이용할 경우 완벽하게 중복로그인을 처리해줄 수 있는 방법은 없다는 의견이 많았고,
최대한 방지할 수 있게 로직을 구성할 수 밖에 없다고 생각했다.


🟢 해결 방안

현재 설계된 인증 방식에서 추가할 수 있는 로직으로 다음과 같은 방법을 생각했다.

1. 로그인 시, Refresh Token만 레디스에 저장하던 로직을 Access Token도 함께 저장

이때, 레디스에는 하나의 key값에 두 개의 value가 저장될 수 없으므로 원래 key값으로 사용되던 email뒤에 "AT"라는 문자열을 붙여서 Access Token을 저장할 key로 사용했다.


2. 로그인 시, 해당 유저에게 발급된 Access Token이 여전히 레디스에 저장되어 있는지 조회하는 로직 추가

Access Token이 만료되었을 경우 레디스에서는 자동 삭제가 되었을 것이다.
하지만 만료되지 않은 토큰일 경우, 레디스에 저장되어 있을 것이고 레디스를 조회해 저장된 토큰이 있을 경우
토큰의 남은 만료시간을 다시 계산해 레디스에 블랙리스트로 새로 저장되도록 했다.

블랙리스트에는 key값으로 Access Token값을, value로 "useless"라는 문자열을 넣어주었다.
유저가 요청을 보냈을 때 블랙리스트로 등록된 토큰인지를 조회해야 하는데, 이 과정은 스프링컨테이너로 넘어오기 전인
security 필터단에서 전역적으로 수행되는 것이 좋다고 판단했다. 필터에서 토큰을 분해하지 않고 토큰 값 자체로 조회하는 것이 더 빠르다 생각해서 key에 토큰값 자체를 넣어주었다.

레디스의 단점 중 하나인 조회할 수 있는 인자가 key밖에 없다는 점을 실감했다...

3. 레디스 블랙리스트에서 Access Token을 조회하는 로직 추가(AuthFilter에 추가)

유저가 토큰 인증이 필요한 요청을 보낼 때마다 해당 토큰이 블랙리스트에 등록된 토큰인지 조회하는 로직을 추가했다.
사실 당근플래너 서비스는 개인화된 서비스이기 때문에 모든 요청이 토큰인증이 필요하다.
그래서 스프링컨테이너로 들어오기 전 필터단에서 전역적으로 수행될 필요가 있었고, AuthFilter에 로직을 추가해주었다.
블랙리스트에 등록된 토큰일 경우, "블랙리스트에 등록된 Access Token입니다."이라는 메세지와 함께 "UNAUTHORIZED" status를 반환하도록 했다. 이 경우, 발생한 예외는 EntryPoint에서 핸들링된다.






사실 이 중복로그인 문제는 UT가 종료될 때쯤 제대로 원인파악이 되었었다.
당시에는 우선 급하게 처리를 하느라 프론트단에서 임시로 중복로그인을 막는 처리를 해두었었다. 그렇게 프로젝트가 종료되었고, 백엔드에서 추가한 로직은 UT가 종료된 후에 추가된 로직이어서 조금 아쉬운 점이 있다.

하지만 구글링해서 나온 방법을 그대로 적용한 것이 아닌 우리 서비스에서 할 수 있는 로직을 구성했다는 점이 재미있었고, 어떻게든 문제를 해결할 수 있다는 자신감을 얻을 수 있었다.

우리 당근플래너서비스는 팀원들 모두 계속해서 애정을 가지고 있는 서비스라, 언젠가 다들 할 수 있는 상황이 되었을 때 서비스를 더 디벨롭시키기로 했으므로(디자이너님까지 동의!) 조만간 테스트 해볼 수 있을 것이란 희망을..!

profile
IT'S YOUR VICTORY

0개의 댓글

관련 채용 정보