쿠키/세션 방식에서의 부담을 없애기 위해 나온 방식이 JSON Web Token 이다. 줄여서 JWT.
사용자가 로그인을 하면 토큰이라는 것을 전해준다.
토큰은 XXXXXX.YYYYYY.ZZZZZZ 이런 형태로 알파벳과 숫자들이 섞여있는데 중간에 마침표(.)가 두군데 들어가있는것이 보인다. 이 마침표를 기준으로 각각 header, payload, verify signature로 구분된다.(1번 헤더, 2번 페이로드, 3번 시그니쳐 라고 하겠다.)
먼저 2번 페이로드는
이걸 Base64로 디코딩해보면 JSON형식으로 여러 정보들이 들어있다.
(이 토큰을 누가 누구에게 발급했는지, 이 토큰이 언제까지 유효한지 그리고 서비스가 사용자에게 이 토큰을 통해 공개하기 원하는 내용, 이를테면 사용자의 닉네임이나 서비스상의 레벨, 관리자 여부 등을 서비스 측에서 원하는대로 담을 수 있다.)
이렇게 토큰에 담긴 사용자 정보 등의 데이터를 Claim이라고 한다. 사용자가 로그인을 하고 나서 받는 토큰에 이 정보들이 클레임이라는걸로 담겨온다.
이게 그 이후 요청들마다 이번에는 사용자로부터 서버한테 보내지는것.
이것의 장점은 사용자가 받아서 갖고 있는 토큰 자체에 이런 정보들이 들어있으면은 서버가 요청마다 일일이 데이터베이스에서 뒤져봐야할 것들이 줄어든다는것.
하지만 특별한 암호화도 아니고 Base64로 인코딩돼있는거면 사용자가 다시 디코딩해서 볼 수 있다는거고 조작해서 악용할 가능성도 있어서 1번과 3번 파트가 필요하다.
1번 헤더
이걸 디코딩하보면 두가지 정보가 담겨있다.
1. type, 토큰의 타입인데 여기에는 언제나 JWT가 들어간다
2. alg 일명 알고리즘. 여기에는 3번 서명값을 만드는데 사용될 알고리즘이 지정됨. HS256 등 여러 암호화 방식 중 하나를 지정할수있다.
1번 헤더와 2번 페이로드, 그리고 '서버에 감춰놓은 비밀 값' 이 셋을 이 암호화 알고리즘에 넣고 돌리면 3번 서명 값이 나오는것.
이 암호화 알고리즘이라는게 한쪽 방향으로는 계산이 돼도 반대쪽으로는 안 되서, 서버만 알고 있는 그 비밀 값을 찾아낼 방법이 없어 토큰 악용이 어렵다.
서버는 요청에 토큰 값이 실려들어오면 1, 2번의 값을 '서버의 비밀 키'와 함께 돌려봐서 계산된 결과값이 3번 서명 값과 일치하는 결과가 나오는지 확인한다.
만약 2번 페이로드의 정보가 서버가 아닌 누군가에 의해 조금이라도 수정되었다면 값이 틀려 거부된다. 이로써 3번 서명 값과 계산값이 일치하고, 유효기간도 지나지 않았다면 그 사용자는 로그인 된 회원으로서 인가를 받는것이다.
서버는 사용자들의 상태를 어디다가 따로 저장해 둘 필요가 없이(stateless), 이 비밀키만 있으면 로그인 요청이 들어올때마다 토큰을 검사해서 사용자들을 걸러낼 수 있다.
사용자에게 발급한 토큰을 회수할수없고 그 토큰의 발급 내역이나 정보를 서버가 어디 기록해서 추적하고 있는것도 아니니까 서버가 가지고있지않아도 되서 편한데 그 토큰을 통제하지는 못한다.
또 어떤 토큰이 해킹당한 경우 가져가버린 토큰을 무효화할 방법도 없기 때문에 만료시간을 가깝게 잡아서 토큰의 수명을 아주 짧게 주는것으로 단점을 보완한다.
<Access token/ Refresh token>
매번 인가를 받을 때 쓰는 수명 짧은 토큰이 엑세스 토큰이고 엑세스 토큰을 재발급받을 때 쓰는거가 리프레시 토큰이다. refresh 토큰은, 상응값을 데이터베이스에도 저장한다.(저장기간 길어도 2주정도)
이렇게 하면 그렇게 하면은 중간에 엑세스 토큰이 탈취당해도 오래 쓰지는 못하고 누구를 강제 로그아웃시킬라면은 리프레시 토큰을 갖다가 DB에서 지워서 토큰갱신이 안 되게 하면 된다.
이런점 때문에 아무리 JWT가 구현하기 편리하고 좋더라도 이를 적용하기에 내 서비스가 적합한지 충분히 고려해야 한다.