๐Ÿ“’ [ TIL ] 2022.06.27_48์ผ์ฐจ # DRF๋ฅผ ์ด์šฉํ•œ JWT ์‚ฌ์šฉํ•˜๊ธฐ

๋ฌธ๋ช…์ฃผยท2022๋…„ 6์›” 27์ผ
0
post-thumbnail

[ 2022-06-27 (์›”) ์˜ค๋Š˜์˜ TIL ]

[ Today Learn ]

  • ์ธ์ฆ์˜ ์ข…๋ฅ˜
  • JWT ์˜ต์…˜ ์„ค์ •ํ•˜๊ธฐ
  • Refresh Token

โœ๏ธ ๋‚ด๊ฐ€ ๋ฐฐ์šด๊ฒƒ, ์–ป์€ ๊ฒƒ

* ์„ธ์…˜ ์ธ์ฆ

๊ธฐ๋ณธ์ ์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ(Authentication)์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์„œ๋ฒ„์—์„œ๋Š” ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ , ๊ทธ ์‘๋‹ต์œผ๋กœ JSESSIONID ๋ผ๋Š” ํ‚ค๋ฅผ ์ด์šฉํ•ด ํด๋ผ์ด์–ธํŠธ(์‚ฌ์šฉ์ž) ๋ธŒ๋ผ์šฐ์ €์˜ ์ฟ ํ‚ค์— ์„ธ์…˜์˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ฒŒ ๋œ๋‹ค. ์ดํ›„ ํด๋ผ์ด์–ธํŠธ๋Š” ๋ธŒ๋ผ์šฐ์ € ์ฟ ํ‚ค์— ์ €์žฅ๋œ JSESSIONID ๋กœ ์ €์žฅ๋œ ์„ธ์…˜ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด
์ธ๊ฐ€(Authrization)๋œ ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

* ํ† ํฐ ์ธ์ฆ ๋ฐฉ์‹

ํ† ํฐ์ธ์ฆ ๋ฐฉ์‹์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด ์„œ๋ฒ„์—์„œ๋Š” ํ† ํฐ์„ ์ƒ์„ฑํ•œ ๋’ค์— ์ €์žฅํ•˜์ง€์•Š๊ณ  ํ† ํฐ๊ฐ’์„ ์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ €์— ์‘๋‹ตํ•˜๊ณ  ํ† ํฐ ๊ฐ’์„ ์‚ฌ์šฉ์ž๊ฐ€ ์ธ๊ฐ€๋œ ์‚ฌ์šฉ์ž๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค๋ฅผ ์š”์ฒญํ•  ๋•Œ ํ•จ๊ป˜ ๋ณด๋‚ด๊ฒŒ ๋˜๊ณ , ์„œ๋ฒ„์—์„œ ์ด ํ† ํฐ์„ ์˜๋ฏธ ์žˆ๋Š” ๊ฐ’(๋ณดํ†ต์€ ์‚ฌ์šฉ์ž ์ •๋ณด)์œผ๋กœ ํ•ด์„ํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๊ฒŒ ๋œ๋‹ค.

๐Ÿงฉ ์ ์šฉ ์˜ˆ์‹œ

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  1. HEADER

JWT๋ฅผ ๊ฒ€์ฆํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง„ ๋ฐ์ดํ„ฐ

๐Ÿงฉ ์ ์šฉ ์˜ˆ์‹œ

HEADER ์ •๋ณด
{
  "typ": "JWT",    # ํ† ํฐ ํƒ€์ž…
  "alg": "HS256"   # ์•Œ๊ณ ๋ฆฌ์ฆ˜
}
  1. PAYLOAD

์‹ค์งˆ์ ์œผ๋กœ ์ธ์ฆ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ฐ๊ฐ์˜ ํ•„๋“œ๋ฅผ ํด๋ ˆ์ž„(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๊ฐ’์ด ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค.
}
  1. VERIFY SIGNATURE

ํ† ํฐ ์ž์ฒด์˜ ์ง„์œ„์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉํ•œ๋‹ค. verify signature ๋Š” Base64UrlEncoding๋œ header์™€ payload์˜ ์ •๋ณด๋ฅผ ํ•ฉ์นœ ๋’ค SECRET_KEY๋ฅผ ์ด์šฉํ•ด Hash๋ฅผ ์ƒ์„ฑํ•ด ์•”ํ˜ธํ™”ํ•œ๋‹ค.

๐Ÿงฉ ์ ์šฉ ์˜ˆ์‹œ

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
	SECRET_KEY
)

* DRF์—์„œ JWT ์‚ฌ์šฉํ•˜๊ธฐ

drf์—์„œ jwt๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€์žฅ ๋จผ์ € simplejwt๋ฅผ ์„ค์น˜ํ•ด์ค€๋‹ค.

pip install djangorestframework-simplejwt
  1. settings.py
INSTALLED_APPS = [
    ...
    'rest_framework_simplejwt',
    ...
]

'DEFAULT_AUTHENTICATION_CLASSES': [
		...
		# JWT ์ธ์ฆ ๋ฐฉ์‹ ์ถ”๊ฐ€ํ•˜๊ธฐ
		'rest_framework_simplejwt.authentication.JWTAuthentication',
],
  1. user/urls.py
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๋ฅผ ํ™œ์šฉํ•ด ๋‚ด๊ฐ€ ํฌํ•จํ•˜๊ณ  ์‹ถ์€ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์ปค์Šคํ…€ํ•ด ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š”๊ฒŒ ์‹ ๊ธฐํ–ˆ๋‹ค.

profile
ํ•˜๋ฃจ ํ•œ๊ฑธ์Œ์”ฉ ๊พธ์ค€ํžˆ ๋‚˜์•„๊ฐ€๋Š” ๊ฐœ๋ฐœ์ž๐Ÿ™†โ€โ™€๏ธ https://github.com/Moonmooj

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด