JWT(JSON Web Token)으로 3행시 해보겠습니다
J : 제이슨 웹 토큰이
W : 왜 중요한지 모른다면
T : 토킹 어바웃
JWT(Json Web Token) 이란
인증에 필요한 정보들을 암호화시킨 JSON 토큰
JWT는 세 부분으로 구성되고 .(점)을 구분자로 나누어지는 세 가지 문자열의 조합
세 가지 문자열?
- Header
- Payload
- Signature
Header
Header는 일반적으로 두 가지 정보를 포함
Header는 Base64URL로 인코딩 되어 생성
- alg : 사용되는 암호화 알고리즘(HMAC SHA256, RSA)
- typ : 토큰의 타입을 나타냄. 일반적으로 "JWT"
{
"alg": "HS256",
"typ": "JWT"
}
Payload(Claims)
실제 전달하고자 하는 정보를 포함
값-쌍 형식으로 이루어진 한 쌍의 정보를 클레임(Claim)이라고 부름
클레임은 일반적으로 세 가지 유형으로 구분
Header와 동일하게 Base64URL로 인코딩 되어 생성
Registered claims
- 표준 클레임 이름들로, 예약되어 있는 의미를 가짐
- 예를들어 'iss'는 발행자, 'exp'는 만료시간, 'sub'는 주제, 'aud'는 대상
Public claims
- 충돌을 방지하기 위해 URL 형식으로 이름이 정의됨
- 공개용 정보 전달을 위해 사용
Private Claims
- 클라이언트와 서버 간에 협의된 이름-값 쌍
- 해당하는 당사자들 간에 정보를 공유하기 위해 만들어진 사용자 지정 클레임
- 외부에 노출되어도 상과없지만, 유저를 특정할 수 있는 정보를 포함
예시
{
"sub": "1234567890", // Registered claim: 'sub'는 주제(Subject)를 나타냄, 보통 고유 식별자가 들어감
"exp": 1615556800, // Registered claim: 'exp'는 만료시간(Expiration Time)을 나타냄, Epoch 시간으로 표현됨
"https://example.com/user_role": "admin", // Public claim: 충돌을 피하기 위해 URI 형태로 이름이 지정됨
"name": "Hgom", // Private claim: 'name'은 특정 어플리케이션에서 정의한 것
"admin": true // Private claim: 'admin' 역시 특정 어플리케이션에서 정의한 것
}
Signature
Header와 Payload를 암호화한 값
생성 과정
- Header와 Payload를 각각 Base64Url 인코딩
- 인코딩 된 Header와 Payload를 연결하고, 이 문자열에 대한 서명을 생성
- 이때 "secret"이라고 하는 서버가 가진 비밀키를 사용
- 암호알고리즘은 Header 정보의 alg 알고리즘을 사용함
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
예시
import hashlib
import hmac
import base64
import json
def base64_url_encode(data):
"""데이터를 Base64로 인코딩하고 URL-safe하게 만듭니다."""
return base64.urlsafe_b64encode(data).rstrip(b'=')
# 헤더 정보
header = {
"alg": "HS256",
"typ": "JWT"
}
# 페이로드 정보
payload = {
"sub": "1234567890",
"name": "Hgom",
"admin": True
}
# 비밀 키
secret = "secret"
# 헤더와 페이로드를 Base64Url 인코딩
encoded_header = base64_url_encode(json.dumps(header).encode())
encoded_payload = base64_url_encode(json.dumps(payload).encode())
# 인코딩된 헤더와 페이로드를 결합
signing_input = encoded_header + b'.' + encoded_payload
# HMAC SHA256 서명 생성
signature = hmac.new(
secret.encode(),
msg=signing_input,
digestmod=hashlib.sha256
).digest()
# 서명을 Base64Url 인코딩
encoded_signature = base64_url_encode(signature)
# 모든 부분을 결합하여 최종 JWT 토큰 생성
jwt_token = signing_input + b'.' + encoded_signature
print("생성된 JWT 토큰:", jwt_token.decode())
JWT 토큰을 사용한 인증 프로세스
1. 사용자 로그인
- 사용자가 자신의 아이디와 비밀번호 등의 인증 정보를 입력하여 서버에 전송
2. 서버에서 인증
- 받은 정보를 확인해서 사용자의 인증 여부 결정
- 인증 성공한 경우, Header, PyaLoad, Signature를 바탕으로 JWT 토큰을 생성함
3. 토큰 발행
- 생성된 JWT 토큰을 사용자에게 반환
- 일반적으로 HTTP 응답 헤더의 "Authorization' 필드에 담겨 전송되거나, 쿠키에 저장할 수도 있음
from flask import Flask, jsonify, request, make_response
import jwt
import datetime
app = Flask(__name__)
# 임시로 사용할 비밀 키
app.config['SECRET_KEY'] = 'mysecretkey'
@app.route('/login', methods=['POST'])
def login():
auth_data = request.get_json()
username = auth_data.get('username')
password = auth_data.get('password')
if username == 'user' and password == 'password':
# JWT 토큰 생성
token = jwt.encode({'user': username, 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)}, app.config['SECRET_KEY'], algorithm='HS256')
# 토큰을 Authorization 헤더에 담아 반환
return make_response(jsonify({'message': 'Token is in the Authorization header'}), 200, {'Authorization': f'Bearer {token.decode("utf-8")}'})
4. 클라이언트 저장
- 클라이언트(웹 브라우저, 모바일 앱 등)는 받은 토큰을 저장함
5. 사용자 인증
- 사용자가 인증이 필요한 서비스에 접근하려 할 때마다, 저장된 토큰을 HTTP 요청 헤더에 담아 서버에 전송
6. 서버에서 토큰 검증
- 서버는 요청 받을 때마다 헤더의 토큰을 검증함. 토큰의 Signature와 만료 시간 등을 체크하여 해당 사용자가 유효한지 확인
7. 요청 처리
토큰이 유효하면, 서버는 요청을 처리함
'알쓸코잡' 카테고리의 다른 글
Race Condition과 Thread.Lock (Feat. 파이썬) (0) | 2023.06.09 |
---|---|
dict.fromkeys()에 관한 사실 (0) | 2023.05.26 |
꼬리에 꼬리를 무는 Proxy 이야기 (0) | 2023.05.08 |
[강좌] 개발자를 위한 ChatGPT 프롬프트 엔지니어링 - Andrew Ng (2) | 2023.04.29 |
꼬리에 꼬리를 무는 웹 스크래핑/크롤링 이야기 (0) | 2023.04.27 |