Backend/Django

[django-rest-framework] (16) JWT 인증

dltjdn 2022. 2. 14. 17:44

Token 인증 VS JWT 인증

DRF Token

  • 단순한 랜덤 문자열로 각 User와 일대일 매칭
  • 유효기간 X
  • Token만으로 어떤 유저인지 알 수 없고 Token에 매칭된 User가 누구인지 데이터베이스에서 찾아야만 알 수 있다

 

JWT ( JSON Web Token )

  • 토큰 자체에 User 데이터를 담을 수 있어 데이터베이스를 조회하지 않아도 User 인증이 가능하다
  • 포맷 : "헤더.내용.서명"  → Header와 payload(내용) : base64인코딩 / signature(서명) : Header와 payload를 조합하고 비밀키로 서명한 후 base64 인코딩
  • 서버에서 토큰 발급 시에 비밀키로 서명을 하고 발급 시간 및 유저 정보 등을 저장
  • 비밀키로 서명 했기에 위조변조가 불가능 ( 비밀키를 잘 관리하는 것이 중요! )
  • 서명은 암호화가 아니기에 누구라도 볼 수 있어 보안성 데이터를 넣지 말고, 최소한의 필요정보만 넣기
  • 비밀키 : 장고 기본 settings.SECRET_KEY를 활용 Or 별도로 JWT_SECRET_KEY 설정을 한다
  • claim : 담는 정보의 한 부분으로 key:value 형식 -> djangorestframework-jwt에서는 Payload 영역에 usre_id, username, email 라는 claim 사용
  • 갱신(Refresh) 매커니즘을 지원 → Token expire time 내에 갱신 Or username/password를 통해 재인증
  • 이미 발급된 Token을 폐기(Revoke) 하는 것은 불가능

≫ 스마트폰 앱은 안전한 저장공간이 제공되지만 웹 브라우저에는 없으므로 일반 Token/JWT 토큰 사용시 안전한 장소에 보관하는 것이 중요! + https 통신 활용하기 ( by SSL인증서,  Let's Encrypt

 

 

 

djangorestframework-jwt

설치 : pip install djangorestframework-jwt

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES' : [
        ...
        'rest_framework_jwt.authentication.JWONWebTokenAuthentication',
    ]
    ....
}
# urls.py

from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token

urlpatterns = [
    path('api-jwt-auth/$', obtain_jwt_token), # jwt 획득
    path('api-jwt-auth/refresh/', refresh_jwt_token), # jwt refresh
    path('api-jwt-auth/verify/', verify_jwt_token), # 현재 client가 가진 토큰이 유효한 token인지 검사
]

HTTPie를 통한 JWT 발급

쉘 > http POST http://주소/api-jwt-auth/ username="유저명" password="암호"

{

                "token" : "인증에 성공할 경우, 토큰 응답이 온다. "

}

 

쉘 > http POST http://주소/api-jwt-auth/verify/ token="토큰"

{

                "token" : "검증에 성공할 경우, 토큰 응답이 온다. "

}

 

발급받은 JWT Token으로 API요청 예시

쉘 > http http://주소/blog/api/post/ "Authorization: JWT 토큰"

c.f ) DRF Token에서는 인증헤더의 시작 문자열로 Token을 썼다 →  "Authorization: Token 토큰" 

 

JWT Token 유효기간

유효기간 : settings.JWT_AUTH의 JWT_EXPIRATION_DELTA디폴트 5분 설정

쉘 > http http://주소/blog/api/post/ "Authorization: JWT 토큰"

HTTP/1.0 401 Unauthorized

{

       "detail" : "Signature has expired"

}

유효기간이 지난 Token은 위와 같이 "401 Unauthorized" 응답을 받는다

  • Token 유효기간 내 : Token만으로 갱신 가능
  • Token 유효기간 지남 : username/password를 통해 다시 인증을 받아야만 한다

 

JWT Token 갱신받기

Token 유효기간 내에만 가능

JWT_ALLOW_REFRESH가 True인 상태에서만 갱신 지원, False인 경우 orig_iat필드를 찾을 수 없다는 오류 뜬다

# settings.py
JWT_AUTH = {
    'JWT_ALLOW_REFRESH' : True,  #default는 False
}

쉘 > http POST http://주소/api-jwt-auth/refresh/ token="토큰"

{

               "token" : "갱신받은 토큰 "

}

 

djangorestframework-jwt의 주요 settings

# settings.py

JWT_AUTH = {
	'JWT_SECRET_KEY' : settings.SECRET_KEY,
    'JWT_ALGORITHM' : 'HS256',
    'JWT_EXPIRATION_DELTA' : datetime.timedelta(seconds=300),
    'JWT_ALLOW_REFRESH' : False,
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7)
}
  • JWT_SECRET_KEY : settings의 SECRET_KEY를 지정할 수도 있지만 jwt의 SECRET_KEY를 별도로 만들어 설정할 수도 있다
  • JWT_EXPIRATION_DELTA : 디폴트 5분 / 이 기간 내에 refresh를 요청한다면 Token 만으로 refresh되고, 기간이 지나서 요청한다면 거부되어 username과 password를 통해 새롭게 token을 받아 expire 타임과 refresh타임도 새로 할당받는다
  • JWT_REFRESH_EXPIRAION_DELTA :  디폴트 7일