HTTP = 인터넷 상에서 데이터를 주고 받기 위한 서버/클라이언트 모델을 따르는 프로토콜.
클라이언트가 서버에게 요청을 보내면 서버는 응답을 보내는 방식으로 데이터를 교환한다. HTTP 는 비연결성 및 무상태성 이라는 특성을 가지고 있다.
HTTP는 요청에 대한 응답을 처리하면 연결을 끊는다. 따라서 클라이언트에 대한 이전의 상태 정보 및 현재 통신 상태가 남아있지 않다.
서버가 다수의 클라이언트와 연결을 유지한다면, 이에 따른 자원 낭비가 심해진다.
하지만 비연결성 및 무상태성의 특징을 가진다면 불필요한 자원 낭비를 줄일 수 있다.
그러나, 서버는 클라이언트를 식별할 수 없다는 단점이 있다. 로그인을 하더라도 다음 요청에서 해당 클라이언트를 기억하지 못해 또 로그인을 해야하는 문제가 발생한다. (=브라우저에서 새로고침을 누를 때 마다 로그인을 해야하는 상황)
하지만 우리가 사용하고 있는 웹 사이트들의 경우 한 번 로그인하면 다시 로그인 할 필요 없이 여러 페이지를 돌아다니며 다양한 기능을 이용 할 수 있다. 심지어 브라우저는 껐다 켜도 로그인이 유지된다. 이는 HTTP의 비연결성 및 무상태성 특징을 보완한 기술인 Cookie 와 Session 덕분이다.
cookie = 클라이언트가 어떤 웹 사이트를 방문할 경우, 그 사이트가 사용하고 있는 서버를 통해 클라이언트의 브라우저에 설치되는 작은 기록 정보 파일.
MockHttpServletResponse:
Status = 200
Headers = [Set-Cookie:"userName=kevin", "password=abc123"]
서버는 클라이언트의 로그인 요청에 대한 응답을 작성할 때, 클라이언트 측에 저장하고 싶은 정보를 응답 헤더의 Set-Cookie에 담습니다. (쿠키는 Key-Value 형식의 문자열이다.)
MockHttpServletRequest:
HTTP Method = GET
Request URI = /user/my/edit
Headers = [Cookie:"userName=kevin"; "password=abc123"]
이후 해당 클라이언트는 요청을 보낼 때 마다, 매번 저장된 쿠키를 요청 헤더의 Cookie
에 담아 보낸다.
서버는 쿠키에 담긴 정보를 바탕으로 해당 요청의 클라이언트가 누군지 식별할 수 있다.
HTTP/1.1 200
Set-Cookie: JSESSIONID=FDB5E30BF20045E8A9AAFC788383680C;
위와 같은 Cookie & Session의 과정속에서 요청을 진행할 때 마다 세션 저장소에서 세션 ID를 조회하는 작업을 통해 DB 접근 로직이 한번 더 추가된다는 점을 보완하기 위해 등장.
JWT(Json Web Token)은 하나의 인터넷 표준 인증 방식이다. 인증에 필요한 정보들을 Token에 담아 암호화시켜 사용하는 토큰이다.
따라서, 기본적인 인증을 진행하는 구조는 Cookie때와 다르지 않지만, JWT는 서명된 토큰이라는 점이다. 공개/개인 키를 쌍으로 사용하여 토큰에 서명할 경우 서명된 토큰은 개인 키를 보유한 서버가 이 서명된 토큰이 정상적인 토큰인지 인증할 수 있다.
이러한 JWT의 구조 덕분에 인증 정보를 담아 안전하게 인증을 시도하게끔 전달할 수 있다.
Header, Payload, Signature 의 구성요소를 가지고 있고 각 구성 요소는 점(.)으로 구분된다.
ex)xxxxx.yyyyyy.zzzzzz 와 같은 형식
{
"typ": "JWT", //토큰 타입
"alg": "HS512" //HS512 알고리즘 적용
}
header에는 보통 토큰의 타입이나, 서명 생성에 어떤 알고리즘이 사용되었는지 저장.
2. Payload
{
"sub": "1",
"iss": "ori",
"exp": 1636989718,
"iat": 1636987918
}
payload에는 보통 **Claim**이라는 사용자에 대한 or 토큰에 대한 property를 key-value의 형태로 저장한다. Claim이라는 말 그대로 토큰에서 사용할 정보의 조각인 셈.
어떤 Claim을 넣을지는 개발자 마음(?)이지만 JWT 표준 스펙은 이렇다.
iss (Issuer) : 토큰 발급자
sub (Subject) : 토큰 제목 - 토큰에서 사용자에 대한 식별값이 됨
aud (Audience) : 토큰 대상자
exp (Expiration Time) : 토큰 만료 시간
nbf (Not Before) : 토큰 활성 날짜 (이 날짜 이전의 토큰은 활성화 되지 않음을 보장)
iat (Issued At) : 토큰 발급 시간
jti (JWT Id) : JWT 토큰 식별자 (issuer가 여러명일 때 이를 구분하기 위한 값)
다만 가장 중요한 것은 payload에 **민감한 정보를 담지 않는다는 것**이다.
header와 payload는 json이 디코딩되어있을 뿐 특별한 암호화가 걸려있는 것이 아니기 때문에 누구나 jwt를 가지고 디코딩을 한다면 header나 payload에 담긴 값을 알 수 있기 때문이다.
[jwt.io](https://jwt.io/)에서 서버에서 생성한 JWT를 넣기만 해도 디코딩된다.
![](https://velog.velcdn.com/images/songtofu/post/653999ff-0461-41c8-950e-13f86610e87f/image.png) (출처: https://jinyoungchoi95.tistory.com/39)
그렇기 때문에 JWT는 단순히 **식별하기 위한 정보**만을 담아두어야 한다.
3. Signature
가장 중요한 서명이다. Header와 Payload를 보여줄 때는 인코딩 되어있던 값들을 JWT에 담겨있는 것 처럼 디코딩된 상태를 사용한다. header를 디코딩한 값, payload를 디코딩한 값을 위처럼 합치고 이를 your-256bit-secret(= 서버가 가지고 있는 개인키를 가지고 암호화되어있는 상태)
따라서, signature는 서버에 있는 개인키로만 암호화를 풀 수 있으므로 다른 클라이언트는 임의로 Signature를 복호화 할 수 없다.
#### 4.1) 장점
쿠키와 세션을 넘어오며 겪었던 모든 단점을 해결하는 것이 JWT이기 때문에, 단점을 뒤집으면 장점이 된다.
1. 이미 토큰 자체가 인증된 정보이기 떄문에 세션 저장소와 같은 별도의 인증 저장소가 "필수적"으로 필요하지 않다.
2. 세션과는 다르게 클라이언트의 사앹를 서버가 저장해두지 않아도 된다.
3. signature를 공통키 개인키 암호화를 통해 막아두었기 떄문에 데이터에 대한 보완성이 늘어남.
4. 다른 서비스에 이용할 수 있는 공통적인 스펙으로 사용할 수 있다.
-> stateful해야하는 세션의 단점을 보완하기 위해 만들어진 JWT는 별도의 세션 저장소를 강제하지 않기 때문에 stateless하여 확장성이 뛰어나고, signature를 통한 보안성까지 갖추고있다.