하루에도 수십, 수백 명의 사용자가 서비스를 이용한다. 이 때 회원 가입 여부에 따라 혹은 관리자 여부에 따라 제공되는 서비스들이 달라진다. 따라서 서버는 동시다발적으로 서비스를 요청하는 사용자가 로그인을 한 상태인지 확인하여 다양한 서비스들을 사용할 수 있도록 응답해줘야한다. 이럴 때 필요한 것이 인증, 인가이다.
인증(Authentication)
: 사용자가 자기 계정을 사용을 하려고 할 때 로그인을 시켜준다.인가(Autorization)
: 인증받은 사용자가 서비스를 사용하려고 할 때 서버가 인증을 받은 사람인지 확인하고 해당 서비스를 사용할 수 있게끔 해준다.인증과 인가를 적용할 수 있는 다양한 방법들이 있는데 먼저 세션이다.
쿠기(브라우저에 저장되는 정보)
로 저장하고 이 브라우저는 앞으로 다음 사이트에 요청을 보낼 때마다 함께 실어서 보낸다.
하지만 이러한 세션에도 허점이 있는데,
메모리가 부족해진다.
저장된 정보가 모두 날아갈 수 있다.
그렇다고 세션을 이용하지 않고 DB에 저장하자니 속도가 느려져서 고안해낸 것이 Redis나 MemCached같은 메모리형 데이터베이스 서버이다. 그런데 똑똑한 어느 개발자들이, 꼭 Redis나 MemCached 같은 서버들을 사용하지 않고도 가능하지 않을까? 라고 생각해서 만들어진 것이 토큰 방식
인 JWT
이다.
type
: jwt임으로 항상 "jwt" 값이 들어있다.alg
: Verify signature를 만드는데 사용될 알고리즘이 지정된다. HS256등 여러 암호화 방식 중 하나를 지정할 수 있다.
공개하기 원하는 내용
(닉네임, 관리자 여부 등)을 담을 수 있다. 이렇게 담긴 데이터들은 Claim이라고 한다.Header, Payload, 서버에 저장해놓은 비밀키를 해당 암호화 알고리즘에 넣고 돌리면 Verify signature값이 나온다.
따라서 Header, Payload의 값들 중 한 글자만 바뀌어도 Verify signature값이 완전히 달라진다.
서버는 받은 jwt토큰값을 서버에 저장되어있는 비밀키와 돌려 계산된 결과값이 Verify signature값과 일치하는 결과가 나오는지 확인
한다. 결과 값이 일치하지 않으면 해커인 걸로 간주되어 요청이 거부된다.
Verify signature값이 일치하고 유효기간이 지나지 않았다면
해당 사용자는 인가를 받을 수 있다.
💡 시간에 따라 바뀌는 상태 값을 받지 않는 걸
stateless
라고 하고(=> jwt), 반대로 바뀌는 상태값을 받는 걸stateful
이라고 한다.(=> 세션)
세션처럼 stateful해서 모든 사용자들의 상태를 기억하고 있다면 기억하는 대상의 상태들을 언제든 제어할 수 있다.
예를 들어, 한 기기에서만 서비스 사용이 가능할 경우 세션에 저장된 정보를 이용하여 새로운 기기에서 로그인하였을 때 기존에 로그인된 기기를 로그아웃 시킬 수 있어야 한다. 그렇다고 JWT를 저장한다면 세션과 다른 바가 없어진다.
JWT 토큰을 해커에게 탈취당한 경우 해당 토큰을 무효화할 방법이 없다. => 이런 상황을 대비해서, 토큰의 만료시간을 가깝게 잡아 토큰의 수명을 짧게한다.
로그인을 할 때 accessToken, refreshToken 두 개를 나누어준다.
- accessToken : 만료시간을 몇 시간 정도로 준다.
- refreshToken : 만료시간이 꽤 길다. 보통 2주 정도이다.
두 토큰을 발급하고 클라이언트에게 보내고 나서 refreshToken은 DB에 저장한다.
회원은 access 토큰의 수명이 다하면 refresh토큰을 보낸다.
서버는 DB에 저장된 값과 대조해보고 맞다면 새 accessToken을 발급해준다.
refreshToken만 안전하게 관리된다면, accessToken이 만료될 때마다 다시 로그인할 필요 없이 새로 발급받을 수 있다. 하지만 accessToken이 만료되기 전까지 바로 차단할 수 없어, 2개의 토큰을 사용하더라도 JWT의 한계는 존재한다.
그런데, 자꾸 JWT가 JMT(존맛탱)처럼 보이는 이유는 뭐지..?🤔