* ์ธ์ ์ธ์ฆ
๊ธฐ๋ณธ์ ์ผ๋ก ๋ธ๋ผ์ฐ์ ์์ ์ฌ์ฉ์๊ฐ ์ธ์ฆ(Authentication)์ ์ํํ๋ฉด ์๋ฒ์์๋ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณ , ๊ทธ ์๋ต์ผ๋ก JSESSIONID ๋ผ๋ ํค๋ฅผ ์ด์ฉํด ํด๋ผ์ด์ธํธ(์ฌ์ฉ์) ๋ธ๋ผ์ฐ์ ์ ์ฟ ํค์ ์ธ์
์ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ฒ ๋๋ค. ์ดํ ํด๋ผ์ด์ธํธ๋ ๋ธ๋ผ์ฐ์ ์ฟ ํค์ ์ ์ฅ๋ JSESSIONID ๋ก ์ ์ฅ๋ ์ธ์
์ ๋ณด๋ฅผ ์ด์ฉํด
์ธ๊ฐ(Authrization)๋ ์ ๋ณด์ ์ ๊ทผํ ์ ์๋ค.
* ํ ํฐ ์ธ์ฆ ๋ฐฉ์
ํ ํฐ์ธ์ฆ ๋ฐฉ์์ ์ฌ์ฉ์๊ฐ ์ธ์ฆ์ ์ํํ๋ฉด ์๋ฒ์์๋ ํ ํฐ์ ์์ฑํ ๋ค์ ์ ์ฅํ์ง์๊ณ ํ ํฐ๊ฐ์ ์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ์ ์๋ตํ๊ณ ํ ํฐ ๊ฐ์ ์ฌ์ฉ์๊ฐ ์ธ๊ฐ๋ ์ฌ์ฉ์๋ง ์ฌ์ฉํ ์ ์๋ ์๋น์ค๋ฅผ ์์ฒญํ ๋ ํจ๊ป ๋ณด๋ด๊ฒ ๋๊ณ , ์๋ฒ์์ ์ด ํ ํฐ์ ์๋ฏธ ์๋ ๊ฐ(๋ณดํต์ ์ฌ์ฉ์ ์ ๋ณด)์ผ๋ก ํด์ํ๊ฒ ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๊ฐ์ผ๋ก ์ฌ์ฉ์๋ฅผ ์ธ์ฆํ๊ฒ ๋๋ค.
๐งฉ ์ ์ฉ ์์
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT๋ฅผ ๊ฒ์ฆํ๋๋ฐ ํ์ํ ์ ๋ณด๋ฅผ ๊ฐ์ง ๋ฐ์ดํฐ
๐งฉ ์ ์ฉ ์์
HEADER ์ ๋ณด
{
"typ": "JWT", # ํ ํฐ ํ์
"alg": "HS256" # ์๊ณ ๋ฆฌ์ฆ
}
์ค์ง์ ์ผ๋ก ์ธ์ฆ์ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๊ฐ๊ฐ์ ํ๋๋ฅผ ํด๋ ์(claim)์ด๋ผ๊ณ ํ๊ณ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ํด๋ ์์ username ๋๋ user_id๋ฅผ ํฌํจํ๊ณ , ์ธ์ฆ์์ payload์ ์๋ username์ ๊ฐ์ ธ์์ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ์ธ์ฆํ ๋ ์ฌ์ฉํ๋ค.
๋ํ payload์์ ์ค์ํ๊ฒ ์ดํด๋ณด์์ผ ํ ์ ๋ณด๋ ํ ํฐ ๋ฐํ์๊ฐ(iat)์ ํ ํฐ๋ง๋ฃ์๊ฐ(exp)์ด๊ณ , ํ ํฐ์ ๋ง๋ฃ์๊ฐ์ด ์ง๋๋ฉด ์๋ก์ด ํ ํฐ์ ๋ฐ๊ธ๋ฐ์์ผ ํ๋ค.
๐งฉ ์ ์ฉ ์์
PAYLOAD ์ ๋ณด
{
"token_type": "access", # ํ ํฐ์ ์ข
๋ฅ. ์ฌ๊ธฐ์๋ access ํ ํฐ์
๋๋ค.
"exp": 1656293275, # ํ ํฐ์ ๋ง๋ฃ์๊ฐ์
๋๋ค. (Numeric Date)
"iat": 1656293095, # ํ ํฐ์ ๋ฐํ์๊ฐ์
๋๋ค. (Numeric Date)
"jti": "2b45ec59cb1e4da591f9f647cbb9f6a3", # json token id ์
๋๋ค.
"user_id": 1 # ์ค์ ์ฌ์ฉ์์ id๊ฐ์ด ๋ค์ด์์ต๋๋ค.
}
ํ ํฐ ์์ฒด์ ์ง์์ฌ๋ถ๋ฅผ ํ๋จํ๋ ์ฉ๋๋ก ์ฌ์ฉํ๋ค. verify signature ๋ Base64UrlEncoding๋ header์ payload์ ์ ๋ณด๋ฅผ ํฉ์น ๋ค SECRET_KEY๋ฅผ ์ด์ฉํด Hash๋ฅผ ์์ฑํด ์ํธํํ๋ค.
๐งฉ ์ ์ฉ ์์
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
SECRET_KEY
)
drf์์ jwt๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ๊ฐ์ฅ ๋จผ์ simplejwt๋ฅผ ์ค์นํด์ค๋ค.
pip install djangorestframework-simplejwt
INSTALLED_APPS = [
...
'rest_framework_simplejwt',
...
]
'DEFAULT_AUTHENTICATION_CLASSES': [
...
# JWT ์ธ์ฆ ๋ฐฉ์ ์ถ๊ฐํ๊ธฐ
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
...
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
...
]
* JWT ์ต์ ์ค์ ํ๊ธฐ
settings.py
from datetime import timedelta
...
SIMPLE_JWT = {
# Access ํ ํฐ ์ ํจ ์๊ฐ ์ค์ ํ๊ธฐ
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
# Refresh ํ ํฐ ์ ํจ ์๊ฐ ์ค์ ํ๊ธฐ
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'LEEWAY': 0,
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
๋ด์ผ ํ๋ก์ ํธ๋ฅผ ์๋๊ณ ์ค๋ ๋ฐฐ์ ๋ ๋ด์ฉ์ ํ์ฉํด์ ํ ํฐ์ ์ด์ฉํ ๋ก๊ทธ์ธ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์๋ค๋ ์๊ฐ์ ์ค๋ ๋ค. ํ ํฐ์ ๋ด๊ธด ์ฌ์ฉ์์ ๋ณด๋ฅผ ์ปค์คํฐ๋ง์ด์งํ ์๋ ์๋ค๋ ๊ฒ์ ์์๊ณ , serializer๋ฅผ ํ์ฉํด ๋ด๊ฐ ํฌํจํ๊ณ ์ถ์ ์ ๋ณด๋ฅผ ์ถ๊ฐํด์ ์ปค์คํ ํด ์ด์ฉํ ์ ์๋ค๋๊ฒ ์ ๊ธฐํ๋ค.