웹 개발에서 로그인 자체를 구현하는 것도 어렵지만, 로그인 성공 이후 로그인 상태를 유지시키는 것도 어려운 문제다.
로그인 상태를 유지시키지 못하면, 사용자는 사이트에서 활동할 때 마다 로그인을 해야 할 것이다.
쉽게 말해서 로그인하는 과정이다.
사용자의 아이디와 비밀번호가 맞으면, 사용자가 해당 사이트의 회원임을 인증하는 과정이다.
인증 받은 사용자가 사이트의 회원으로써 다양한 활동들을 할 수 있게 허가해주는 것.
로그인 이 후의 과정이다.
전통적인 방법으로 사용자가 로그인 하면 서버는 사용자에게 티켓을 준다. 이 때 그냥 주는게 아니라 반을 찢어서 반쪽은 사용자가 갖고, 반쪽은 서버가 갖는다.
서버는 반쪽 티켓을 메모리, 하드디스크, 데이터베이스에 보관하는데 메모리를 책상, 하드를 서랍, 데이터베이스를 창고라고 비유하면 이해하기 쉽다.
사용자 쪽 브라우저는 받은 티켓을 Session ID라는 이름의 쿠키(브라우저에서 저장되는 정보)로 저장하는데, 브라우저는 해당 사이트에 요청(request)를 보낼 때 마다 티켓을 보낸다.
해당 사이트의 서버는 브라우저가 보낸 반쪽 티켓과 서버가 보관한 반쪽 티켓을 비교하면서 일치하면 인증 인가 해준다.
이 때 서버가 반쪽 티켓을 메모리에 보관하면, 많은 사용자가 접속할 때 메모리가 가득 차서 속도가 느려질 수 있다. 또한 서버에 오류가 나거나 연결이 끊기면, 메모리에 저장된 데이터가 사라질 수도 있다.
서랍과 비슷한 하드에 넣어놓으면, 필요할 때 마다 데이터를 참조하기 위해 티켓을 불러와야 되서 불편하다. 마치 서랍을 계속 열고 닫고 하는 것과 마찬가지다.
서버를 여러 대 두고 로드밸런싱을 하면, 모든 서버가 티켓을 가지고 있어야 해서 구현하기 매우 번거롭다.
티켓을 반으로 찢어 주는게 아니라, 티켓 하나를 사용자에게만 줘서 서버는 티켓을 가지고 있지 않아도 된다.
JWT는 암호화된 데이터 세 가지를 이어붙인 것으로 대략 XXXX.YYYY.ZZZ 이런 모습을 가진다.
마침표를 기준으로 세 부분이 나눠지는데 각각 header, payload, verify signature로 나뉜다.
payload는 토큰에 대한 정보, 그리고 토큰이 담고 있는 사용자 정보를 가지고 있다. 예를 들어 토큰의 유효기간, 토큰을 누가 누구에게 발급했고 토큰으로 어떤 권한을 가질 수 있는지에 대한 정보가 들어있다.
payload만으로 토큰을 발행하면, payload를 분석해서 해킹하기 쉬워서 안전장치를 두 개 더 만들었다.
header는 두 개의 정보를 가지고 있다. 첫번째는 Type인데 이것은 항상 JWT가 된다. 두번째는 alg(알고리즘)으로 verify signature을 만들 때 사용된다.
서버는 header와 payload의 정보를 계산해서 나온 값이 verify signature와 일치하면 인증 인가 해준다. 또한 토큰의 유효기간도 지나면 안된다.
만약 payload의 정보가 해킹당해서 잘못되어도, 서버가 계산해서 인증 인가 못하게 막을 수 있다.
JWT 토큰은 열쇠로 비유할 수 있다. 열쇠를 가지면 문을 쉽게 열 수 있다. 따라서 토큰을 쉽게 해킹할 수 없게 안전 장치를 둬야 한다.
JWT는 해킹을 최대한 피하기 위해서 토큰을 두 개 준다. 하나는 access 토큰으로 유효기간이 몇시간이나 며칠 단위로 짧고, 다른 하나는 refresh 토큰으로 보통 2주 정도 여유로운 유효기간을 가진 토큰이다.
보통 refresh 토큰을 데이터베이스에 저장하고, 사용자가 access 토큰을 서버에 주면 refresh 토큰과 비교해서 사용자의 정보를 확인한 후에 인증 인가 한다.
access 토큰은 매번 인증 인가 받을 때 사용되고, 만약 access 토큰의 유효기간이 지나면 refresh 토큰을 이용해서 새로 access 토큰이 발급된다.
하지만 이것도 완벽한 방법이 아닌게 access 토큰이 살아있는 짧은 순간에 해킹 당하면 모든게 뚫릴 수 있다.
결국 보안성이 높지만 서버 관리가 중요한 세션과 보안성이 낮지만 서버 관리가 덜 중요한 JWT 중 어떤 걸 사용할지 고민해야 한다.
사용자가 이메일, 비밀번호를 입력하면 각각의 정보를 각각의 state에 저장한다.
버튼을 누르면 fetch 함수를 통해 서버에 로그인 요청을 보낸다.
fetch('api주소', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: "example@wecode.com",
password: "1q2w3e4r",
})
})
.then(response=>response.json())
.then(result=>console.log(result))
출처 : 얄팍한 코딩사전 : https://www.youtube.com/watch?v=1QiOXWEbqYQ