티스토리 뷰
자, 이제 드디어 로그인을 해야하는 상황이 도래했다.
사실 요즘 새로 시작하는 것들이 많아서, 그리고 회사일이 많아서 글 올라가는 타이밍이 점점 밀리는데 이런때일 수록 힘을 내서 글을 올려야지.
오늘은 로그인을 위해 JWT 토큰을 발급받고 이걸로 권한이 필요한 API를 호출해보는 작업을 진행할 예정이다.
이전부터 자꾸 JWT를 이야기 하면서 로그인하려면 이걸 해야한다고 말했지만 정작 JWT에 대해서 제대로 이야기 해준적이 없어서 JWT에 대해서 먼저 알아보도록 하자.
JWT(JSON Web Token)
일단 공식 사이트는 https://jwt.io/ 여기에 가면 된다. 영어가 되는 분들은 여기가면 아주 상세하고 자세한 JWT에 대한 정보를 얻을 수 있다. 하지만 나같이 영어에 대해 알러지가 있는 사람들은 아래의 설명을 읽어주면 되겠다.
JSON Web Token이란 개체(Client - Client, Client - Server)간 JSON 객체를 통해 작고 독립적인 방법으로 정보를 전송할 수 있는 공개표준(RFC7591)이다. 이 정보는 디지털로 서명되어있어서 검증되고 신뢰할 수 있다. JWT는 HMAC(SHA)알고리즘 or RSA or ECDSA를 사용하는 공용 / 개인 키 쌍을 사용해 서명이 가능하다.
한마디로 JWT에는 정보가 있고 이 정보의 신뢰도는 "서명"에 있다는 이야기가 된다.
그리고 이 서명에 들어가는 키는 공용, 개인키를 이용해 서명이 가능해서 이 서명의 신뢰도를 보장하게 된다는 이야기다.
이 서명이 없거나 있더라도 키가 맞지 않는 경우에는 전달된 데이터를 신뢰할 수 없게 된다는 이야기.
그래서 우리가 만드려는 짤방검색기에서는 로그인을 하면 JWT 정보를 전달할 것이고 이 정보의 유효성을 통해 사용자가 정상 사용자인지 아니면 비정상 사용자인지 구분하는 근거로 사용한다.
위의 이미지처럼 로그인을 통해 JSON Web Token 을 획득한 뒤 API 요청시 발급받은 JSON Web Token 을 요청 헤더에 포함시켜 전달하면 해당 토큰의 유효성을 확인하고 요청한 정보를 응답하는 과정을 거치게 된다.
JWT가 뭔지 알았으니 이제 우리 짤방검색기에 JWT를 적용해보도록 하자.
requirements.txt 파일을 보면 내가 이미 djangorestframework-jwt 라이브러리를 포함했음을 확인할 수 있다.
만약 본인이 djangorestframework-jwt를 포함하지 않았다면 포함시켜서 "pip install -r requirements.txt"을 실행하도록 하자.
(.env) $ pip install -r requirements.txt
funny_picture/settings.py
#
# 생략
#
REST_FRAMEWORK = {
'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S",
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
)
}
#
# 생략
#
일단 settings.py 파일 하단에 REST_FRAMEWORK 설정 부분에 위와 같이 수정하도록 한다.
DEFAULT_PERMISSION_CLASSES의 경우는 API를 요청하는 기본 권한의 수준을 결정할 수 있게 하는데 나는 "AllowAny"로 설정했다. 아무나 접근 가능한 것으로 설정했다.
그리고 DEFAULT_AUTHENTICATION_CLASSES의 경우에 JSONWebTokenAuthentication을 적용한 것을 확인 할 수 있다.
#
# 생략
#
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=168),
'JWT_ALLOW_REFRESH': True,
'JWT_VERIFY': True,
'JWT_AUTH_HEADER_PREFIX': 'FNY'
}
그리고 위의 코드를 설정에 추가한다. JWT_EXPIRATION_DELTA는 토큰의 유효시간을 어떻게 할 것인지 설정한다. 나의 경우엔 168시간(7일)로 설정했는데 사실 짧으면 짧을수록 보안에 좋겠지만 짧을 수록 토큰을 갱신하기 위해 로그인을 자주해야하는 이슈가 있다. 이 것은 사이트의 운영방식에 따라 달라질 수 있으니 알아서 설정하자. 어쨌든 나는 7일로 결정했다.
JWT_ALLOW_REFRESH의 경우 토큰 새로고침 여부를 설정한다. 기본값은 False 다. 새로고침을 할 수 있도록 설정하면 토큰을 새로고침하여 유효시간을 재설정할 수 있도록 토큰을 자동으로 재생성 할 수 있다.
JWT_VERIFY 는 토큰의 유효성을 검사할 수 있는 API 를 제공받을 수 있다.
JWT_AUTH_HEADER_PREFIX는 API를 요청할 때 토큰을 헤더에 포함해 던질때 토큰 앞부분에 붙일 텍스트를 설정하는 거다. 잘 모르겠으면 나중에 내가 적용해보는 것을 확인해보면 되겠다.
이제 토큰을 요청할 URL을 설정해보자.
account/urls.py
from django.conf.urls import url
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token
from .views import *
urlpatterns = [
url(r'^v1/hello', HelloView.as_view(), name=HelloView.name)
, url(r'^v1/get-user', UserInfo.as_view(), name=UserInfo.name)
, url(r'^v1/get-auth-token', obtain_jwt_token) # 사용자 로그인(토큰발급)
, url(r'^v1/token-verify', verify_jwt_token) # 사용자 토큰 유효성 검사
, url(r'^v1/token-refresh', refresh_jwt_token) # 사용자 토큰 갱신
]
account/urls.py 파일을 위와같이 수정하자. 각각의 항목에 대한 설명은 옆에 주석으로 달아놨으니까 별도로 설명은 하지 않는걸로.
그리고 PostMan이라는 프로그램을 설치하자.
https://www.getpostman.com/downloads/ 여기에 가서 다운로드 받으면 된다.
다운로드 버튼 누르고 자신의 운영체제에 맞게 다운로드 하면 된다. 나는 64비트 운영체제니까 64비트를 다운로드 받았다. Post man이라는 프로그램에 대해서 설명하자면 API 서버를 만들때 Javascript를 이용해서 Ajax 통신 테스트 코드를 만들기 귀찮으니 이 프로그램으로 대체하여 대신 API를 테스트해보는 그런 프로그램이라고 생각하면 되겠다.
꽤 훌륭한 프로그램이니 다운로드 받아서 활용하도록 하자. 이 외에도 이런 종류의 프로그램은 몇가지가 있으니 이 프로그램이 싫다면 다른 프로그램을 사용해도 괜찮다.(아, 외국 서버라서 다운로드가 굉장히 느릴 수 있다.)
다운로드를 받는데 너무 오래걸려서(약 12시간정도...) 크롬 확장프로그램으로 "Restlet" 이라는 프로그램이 있어서 이것으로 대신 다운로드 받았다.(https://restlet.com/modules/client/)
이런건 기능이 다들 고만고만해서 아무거나 써도 상관없다.
여기에서 아래와 같이 셋팅하도록 하자
Method는 "POST"로, URL입력하는 곳에는 "http://127.0.0.1:8000/v1/get-auth-token", 그리고 Body쪽에는 "Form"으로 변경한 뒤에 "Add form parameter" 버튼을 눌러서 항목을 추가하면 되겠다. 내용은 전에 입력했었던 사용자의 이메일과 패스워드다. 우리는 이메일을 아이디 처럼 사용하기로 했으니까 id 대신 email을 항목으로 전달한다.
셋팅을 마치고 난 뒤에 "Send" 버튼을 눌러주면 Response 쪽에서 응답으로 돌려받은 데이터를 확인할 수 있는데 Body쪽에 "token" 값이 전달된 것을 확인할 수 있다.
이렇게 된다면 JWT 를 발급받은 거다. 이제 API를 하나 만들어서 이 토큰을 통해 정상적으로 데이터를 가져올 수 있는지 확인해보자.
account/view.py 를 아래와 같이 수정하자.
account/view.py
from django.shortcuts import render
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from .serializers import UserSerializer
from .models import User
class HelloView(APIView):
name = 'hello-view'
def get(self, request, *args, **kwargs):
return Response({'hello': 'world!'})
class UserInfo(APIView):
name = 'user-info'
permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs):
user_id = request.query_params.get("id")
query = User.objects.get(id=user_id)
serializer = UserSerializer(query, many=False)
return Response( serializer.data )
다른거 볼것 없이 permission_classes에 IsAuthenticated 가 적용된걸 확인하면 된다. 물론 제일 위에 IsAuthenticated가 import 된것도 확인해야겠지만. 일단 이렇게 적용하면 UserInfo API는 로그인한 사용자에게만 접근이 허용된다. 그래서 이전 처럼 그냥 데이터를 호출하면 아래와 같은 결과를 확인할 수 있다.
자격 인증데이터가 없다고 401에러를 띄워버린다. 401에러는 자격없는 사람이 접근했을 때 발생하는 에러다. 그러면 아까의 로그인을 한번 실행 한뒤에 토큰을 포함해서 정보를 요청해보도록 하자.
HEADERS 쪽에 Authorization를 추가해주는데 값으로 아까 받은 토큰 값 앞에 "FNY"라는 접미사를 포함시켜준다. 이 값은 아까 settings.py에 설정값으로 넣었던 거다. "FNY" 넣고 한칸 띄고 토큰을 넣어준 다음에 "Send" 버튼을 클릭해주면 정상적으로 데이터를 불러오는 것을 확인할 수 있다.
자, 이렇게 JWT를 활용하여 로그인 처리하는 것까지 알아봤다.
다음 시간에는 JWT의 소소한 몇가지 추가 세팅을 마무리하고 슬슬 관리자 페이지를 만들어보는 시간을 갖도록 하자.
그럼 오늘은 여기까지!
안녕!
'Dev > 프로그래밍 잘하기' 카테고리의 다른 글
[짤방검색기] 14. JWT 세팅 마무리하기 (0) | 2019.07.03 |
---|---|
[짤방 검색기] #12. 사용자 정보 Serializing 하기 (0) | 2019.06.17 |
[짤방 검색기] #11. Django 사용자 정보 만들기(下) (2) | 2019.06.13 |
[짤방 검색기] #10. Django 사용자 정보 만들기(上) (0) | 2019.06.11 |
[짤방 검색기]#9. 본격 개발 준비 중 - 클래스란 무엇인가요? (0) | 2019.06.11 |