이전까지의 프로젝트에서는 Session을 이용해서 인증을 했다. 이번에 새로운 프로젝트를 진행하면서 Spring Security를 사용하게 되었고, 전부터 눈여겨 보았던 JWT를 사용해보기로 했다.
JWT 방식과 session 방식의 차이점을 알아보자.
사용자 로그인 시 로그인 정보를 서버의 세션 저장소에 저장한다. 그리고 저장한 로그인 정보를 찾을 수 있는 key인 세션ID를 쿠키에 저장해서 클라이언트로 보낸다.
이렇게 사용자는 쿠키에 저장된 세션ID를 요청마다 서버로 보내 서버에 저장되어있는 로그인 정보를 찾는 방식으로 인증을 한다.
JWT(Json Web Token)은 인증에 필요한 정보를 암호화시킨 토큰을 사용하는 방식이다. 세션 방식과 유사하게 JWT 토큰을 HTTP 헤더에 실어 서버가 클라이언트를 식별한다.
JWT는 '.'을 구분자로 아래와 같이 세 부분으로 나누어져 있다.
JWT 구조에 대해서는 자세히 알 필요가 없을것 같다. JWT관련 라이브러리가 다 알아서 처리해주기 때문에 로직만 잘 짜면 될 것 같았다.
클라이언트에서 로그인 요청이 들어오면 클라이언트의 고유 ID 등을 payload에 담고, 비밀키를 사용하여 AccessToken(JWT)를 발급하여 클라이언트로 전달한다.
클라이언트는 전달받은 토큰을 안전한 저장소(나중에 언급하게 됨)에 저장해둔다.
클라이언트가 서버에 요청을 할 때마다 토큰을 전달한다.
서버는 전달받은 토큰의 위변조 여부, 유효한 토큰인지 판단을 하고 유효하다면 요청에 응답한다.
토큰의 만료 기한을 짧게 설정한다면 토큰이 탈취되더라도 피해를 줄일 수 있다.
하지만 만료될때마다 매번 다시 로그인을 해줘야하는 엄청난 불편함이 발생한다.
토큰의 만료 기한을 짧게 설정한 탓에 로그인을 해줘야하는 불편함을 해결할 수 있는 방법이다.
이 방식은 AccessToken의 만료 기한을 짧게두고, RefreshToken의 만료기한을 길게 두어, AccessToken이 만료되었다면 이에 대응되는 RefreshToken을 통해 새로운 AccessToken을 발급해주는 방식이다.
이 방식은 RefreshToken이 서버 측의 별도의 저장소(redis 사용)에 저장되어야한다. 이렇게되면 JWT 방식이 갖는 빠른 인증처리라는 장점이 흐려지게된다.
하지만 RefreshToken에 대한 만료를 강제할 수 있고 추가적인 AccessToken의 재발급을 제어할 수 있다.
토큰의 저장은 세션 스토리지, 로컬 스토리지, 쿠키 등을 고려해볼 수 있다.
이러한 방식들은 XSS 공격, CSRF 공격 등에 노출될 수 있다. 그렇다면 각 방식은 어떤 장단점을 가지고 있을까?
다양한 블로그의 글을 읽어 봤는데 어떤 방식이 최선이라고 단언할 수 없을만큼 각각 선호하는 방식이 달랐다. 하지만 이러한 장단점을 미루어 보았을때 나는 Cookie방식이 조금 더 안전하다고 생각해 Cookie 방식을 채택하기로 했다.
이렇게 기존에 사용하던 Session 방식과 JWT는 어떤 차이가 있는지 알아보았다.
다음 포스팅에서는 실제 프로젝트에서 어떻게 JWT를 적용했는지, 앞서 언급한 RefreshToken이 어떻게 사용되는지에 대해서 자세하게 작성할 예정이다.
사실 JWT를 구현하는 사람마다 각기 다른 방식으로 구현하고 어떤게 옳은 방법인지 명확하지 않아 이 글의 내용이 정석이라고 말 할 수는 없다. JWT를 공부하고 구현하면서 수십개의 글을 읽고도 명확한 답이 나오지 않았었고 나름 최선이라고 생각되는 방식으로 구현해나갔다. 나와 같은 사람이 많지않을까 하는 생각으로 내가 어떤 고민을 했고 어떤 근거로 선택을 했는지 써내려갈 것이고 많은 이에게 도움이 되었으면하는 바램이다.