왜 갑자기 HTTP에 대해서 나오는지 궁금할 수 있다.
"로그인" 이라는 과정은 서버와 클라이언트가 데이터를 주고 받는 통신 방법이 필요하다.
그 통신 방법이 HTTP 라는 프로토콜(통신 방법)이다.
HTTP에는 몇 가지 특성이 있다.
HTTP는 연결을 유지하지 않는다. 라는 특성이다.
서버와 클라이언트가 한번 통신이 일어나고 나면
그 연결이 유지가 되는 것이 아니라 바로 끊어진다.
HTTP는 상태를 유지하지 않는다. 라는 특성이다.
서버와 클라이언트는 첫번째 통신을 하고 나서 두번째 통신을 할 때는
이전의 통신에 대한 정보를 가지고 있지 않기 때문에 새롭게 갱신을 해주어야 한다.
위의 특성들 때문에 우리가 "로그인"을 구현하는데 많은 어려움이 있다.
사용자가 매번 서버에 요청을 보낼 때 마다
자신이 누구인지 계속해서 인증을 해주어야 한다는 것.
매번 서버에 사용자가 누구인지 인증을 하는 과정은 번거롭고 귀찮을 뿐만 아니라
매번 요청을 보내야 하기 때문에 우리의 웹페이지가 느려지는 원인이 된다.
위의 원인과 이유로 우리는
사용자가 누구인지 계속해서 인증을 하는 방법 대신
다른 로그인 정보를 유지시킬 방법이 필요했다.
아래 방법들은 로그인을 유지 시킬 방법이라고 생각해도 무방하다
위에서 얘기한대로 유저에게 ID / Password 를 받아서, 서버의 데이터베이스와 그냥 비교하는 방법이 있다.
해당 방법은 로그인 유지가 안될 뿐더러
정보가 유지되지 않으면, 매번 페이지를 이동할 때마다 로그인을 다시 하거나,
상품을 선택했는데 구매 페이지에서 선택한 상품의 정보가 없거나 하는 등의 일이 발생할 수 있다.
무언가 다른, 로그인을 유지할 수 있고 안전한 방법이 필요했다.
Session (세션) vs Token (토큰)
사용자의 로그인 정보에 대한 것을 어디다가 유지, 저장을 하느냐에 따라 두 방식으로 나뉜다.
세션 방식은 서버의 메모리, 데이터베이스와 같은 서버의 자원들을 사용해서
사용자의 정보를 유지시키는 방식이다.
토큰방식보다 보안에 강하다는 장점이 있지만
단점으로는 서버의 확장성이 떨어지고, 서버의 자원(세션을 저장, 유지할 공간)이 많이 필요하다.
또한 세션이 서버에 저장이 되고, 트래픽 분산을 위해서 여러 대의 서버를 사용할 때
만약 사용자가 로그인을 했을 때는 만들어진 세션을 참조해야 하기 때문에
처음 로그인한 그 서버에서만 요청을 보내야 한다는 단점이 있다.
이러한 이유 때문에 아래와 같은 토큰 방식을 사용한다.
토큰 방식은 사용자가 로그인을 하면 서버에서 발행해주는 토큰을 가지고 브라우저의 저장소에 토큰을 유지시키는 방법이다.
여기서 말하는 토큰이 우리가 말하는 JWT이다
서버에 저장하지 않아서 서버에 확장성이 있다.
위에서 말했듯 로그인을 했을 때 해당 서버에만 요청을 보내는 것이 아닌
요청이 들어왔을 때 해당 토큰이 유효한지만 체크 하면 되기 때문에
어떤 서버로 요청을 보내도 상관이 없다.
JWT는 두 개체 사이에서 안정성 있게 정보를 교환하기 좋은 방법
JWT는 우리가 위에서 계속 말한 웹 토큰
, json web token
의 약자이다.
JWT는 웹표준(RFC 7519) 으로,
전자 서명된 URL-safe(URL로 이용할 수 있는 문자만 구성된)의 JSON이다.
jwt
는 .
을 기준으로 세 파트로 나뉜다.
Header
{
"alg": "서명 시 사용하는 알고리즘",
"kid": "서명 시 사용하는 키를 식별하는 값",
"typ": "타입"
}
헤더에는 jwt를 어떻게 검증하는지에 대한 내용이 들어가 있다.
토큰의 타입 , 암호화 알고리즘이 어떤 알고리즘인지에 대한 정보가 들어 있다. 해시 알고리즘의 이름을 적어줄 수 있다.
payload
{
"sub": "hyeonsu.jung",
"exp": 1623235123,
"iat": 1532341234
}
토큰에 담아서 우리가 보내고자 하는 데이터가 이곳에 담겨져 있다.
이 정보의 조각은 클레임
이라고 하고 , key - value
의 한쌍으로 이루어져 있다.
그리고 payload
에는 여러개의 클레임을 담을수 있고, 클레임을 공개 혹은 비공개 할 것 인지 등록 할 것인지 결정할 수 있다.
signature
시그니처(signature)에는 위의 헤더(header)와 페이로드(Paylaod)를 합친 문자열을 서명한 값이다. 서명은 헤더의 alg에 정의된 알고리즘과 secret key를 이용해 생성하고 Base64 URL-Safe로 인코딩한다.
secret key를 포함해서 암호화가 되어있다.
생성된 jwt는 왼쪽의 Encoded된 값처럼 (.)을 구분자로 헤더 , 페이로드 , 시그니처로 나눠서 전달 하게 되고 , 서버는 헤더의 alg , kid 속성과 공개 키를 이용해 검증 할 수 있다. 검증이 성공하면 페이로드의 값으로 접근을 해서 사용 할 수 있는것.
jwt에서는 기본적으로 공개 키 암호방식(PKC,Public Key Cryptography)을 사용하는데 비대칭 암호방식을 이용해 공개 키와 비밀 키를 생성하고 이 키들을 상황에 따라 나누어 가지고 통신한다.
서명은 비밀 키가 있는곳에서만 할 수 있고 공개 키를 가진 어느 곳에서나 이 데이터의 서명을 검증 할 수 있다.
❌ 비밀 키로 암호화한 데이터는 다시 비밀 키로 풀 수 없고,
❌ 공개 키로 암호화 한 다시 데이터는 공개 키로 풀 수 없다.
🟢 서명: 비밀 키를 가진 극소수(주로 한명)만 데이터에 서명할 수 있다. 공개 키를 가진 아무나 데이터의 서명을 검증할 수 있다.
🟢 암호화: 공개 키를 가진 아무나 데이터를 암호화할 수 있다. 비밀 키를 가진 극소수만 데이터를 복호화해 확인할 수 있다.
세션 방식과 다르게 별도의 인증 저장소가 필요 없어서 서버와의 커뮤니케이션을 최소한으로 할 수 있다.
트래픽에 대한 부담이 적다.
세션과 다르게 독립적인 느낌의 JWT를 활용한다는 것.
JWT의 크기가 커질수록 거의 모든 요청에 대해 전송되므로 데이터 트래픽 크기에 영향을 미칠 수 있다.
토큰은 클라이언트에 저장되기 때문에 DB에서 사용자 정보를 수정하더라도 토큰에 직접 적용할 수 없다.
사용자가 id
와password
를 입력하고 서버로 로그인 요청을 보낸다.
서버는 비밀키
를 통해서 서명을 하고 공개 키로 암호화 시킨 Access Token
을 발급한다.
3.Access Token
을 사용자에게 보낸다.
요기까지하면 사용자는 로그인이 된 것이다.
4.로그인 정보가 필요한 API Call마다 토큰을 실어서 보낸다.
사용자(클라이언트)는 API를 요청할 떄
Authorization Header
에Access Token
을 담아서 보낸다.
서버는
secret key
로 보낸 토큰의 서명을 복호화 하여 유효한 토큰인지 확인한다.
6.서버가 요청에 대한 응답을 사용자(클라이언트)에게 전달한다.
Access Token
만을 사용했을 때 보안 문제를 해결하기 위해 나온 Refresh Token
위와 같은 방식으로 진행했을 때의 문제점은 Access Token을 탈취 당했을 때이다.
유효기간이 긴 토큰이라면, 그 시간동안 정보를 탈취당하게 되고
또 유효기간을 줄이자니 사용자가 로그인을 여러 번해야 하는 번거로움이 있다.
그래서 나온 것이 Refresh Token
Refresh Token 또한 Access Token과 같은 JWT이다.
로그인을 했을 때 서버에서 Access Token, Refresh Token을 동시에 보내준다
단, 둘의 유효기간을 다르게 해서 보낸다.
Refresh Token을 한 달, Access Token 을 하루로 잡았다면
Access Token의 기간이 다 되어도 Refresh Token의 기간이 남아있기 때문에
사용자는 로그인 없이 다시 Access Token을 발급 받을 수 있다. (로그인 유지)
Refresh Token는 Access Token를 다시 발급받기 위한 JWT.
토큰을 유지하는 방법에는 다양한 브라우저 저장소가 있다.
secure httpOnly 쿠키, 로컬 스토리지, 쿠키와 같이 다양한데
이것은 개발자의 취향과 장단점이 서로 다르다.
localStorage
저장 방식은가볍게 사용하기 좋으나 기능이 많지 않은 키 밸류 저장소 키와 밸류는 스트링 타입만 지원하며 , 값을 저장할 용량이 크지 않다 5mb~10mb정도
데이터를 만료기간없이 저장 할 수 있다는게 장점
SessionStorage
저장 방식은
Api사용 방법은 로컬과 동일하다 (setitem..getitem..등등)
대신에 데이터를 만료기간은 세션스토리지는 브라우저를 닫거나 새로 열때마다 세션스토리지는 초기화된다 하지만 같은 탭에서 새로고침을 할때는 유지가 된다
[뭔가 자동 임시저장 용도나 , 은행개인정보 시스템에 사용]
쿠키
저장 방식은
웹에서 가장 오래된 웹 저장소 더 적은 용량 kb단위 ..
그래야하는 이유가있다 쿠키는 서버의 데이터를 공유만 해야하는 용도로 만들어졌기 때문이며 계속 사용자와 서버가 주고 받아야하기때문에
서버요청할때마다 같이 가야하는 쿠키가 너무 크면 서비스 속도 저하같은 문제가 발생할수 있다 유효기간 지정 가능하다
각자의 장점이 있기에 서비스의 사용 목적에 따라 저장소 위치를 설정해야한다!
HTTP의 connectionless, stateless 특성에 의해
로그인 정보를 유지시키기 위해서 알맞은 유지 방법이 필요했다.
JWT는 암호화, 복호화를 통해 두 개체 사이에서 정보를 안전하게 주고 받을 수 있는 좋은 수단이다.
Refresh API
새로고침, AccessToken 만료시에 호출 (setTimeout으로 자동 호출 설정 가능)
RefreshToken을 쿠키에서 읽어와서 서버로 보냄
RefreshToken,AccessToken을 다 받아올지 AccessToken만 받아올지는 선택
Login API
로그인 시 호출
RefreshToken,AccessToken 을 받아옴
API 호출 후
AccessToken는 header에 default로 설정하여 API마다 보내도록 설정
AccessToken의 유효기간이 끝나기 전 자동으로 Refresh API가 호출되도록 설정 가능 (선택적)
RefreshToken는 쿠키에 저장
RefreshToken 또한 기간이 만료되면 재로그인이 필요!