Westagram_4

Jina·2020년 4월 16일
0

Django

목록 보기
9/11

앞에 만들어둔 Westagram에 인증과 인가 적용하기

bcrypt와 pyjwt는 여기 참고

password 암호화하기

회원가입 할 때 request.body로 들어오는 password를 암호화해서 저장하기

아래와 같이 저장

# account/models.py

 import json
 import bcrypt	# bcrypt import해주기

 from django.views import View
 from django.http import JsonResponse, HttpResponse
 from django.db import IntegrityError

 from .models import Account

 class AccountView(View):
      def post(self,request):
          data = json.loads(request.body)
          try:
              Account.objects.create(
                          name = data['name'],
                          email = data['email'],
                          password = bcrypt.hashpw(data['password'].encode('utf-8'),bcrypt.gensalt()).decode('utf-8')	# 1. 암호화하기
                           ) 
              return HttpResponse(status=200 )
          except IntegrityError:	# 중복가입 방지
              return HttpResponse(status=400)

1) 암호화하기
request.body에서 password를 받고( data['password'] ) 그 password를 인코드하여 ( data['password'].encode('utf-8') ) 바이트화 시킴
그 이후 bcrypt.hashpw를 이용하여 암호화하고( bcrypt.hashpw(data['password']. encode('utf-8'),bcrypt.gensalt()) ) 저장을 위해서 decoding함

토큰 만들기

로그인하면 토큰 만들어서 프론트엔드에게 전해주기

아래와 같이 토큰 만들 수 있음

# account/models.py

 import json
 import bcrypt
 import jwt

 from django.views import View
 from django.http import JsonResponse, HttpResponse
 from django.db.models import Q

 from test1.settings import SECRET_KEY
 from .models import Account

class LoginView(View):
     def post(self,request):
         data = json.loads(request.body)
         password = data.get('password', None)
         email = data.get('email', None)
         name = data.get('name', None)
         if Account.objects.filter(Q(email=email)|Q(name=name)).exists():	# 1. 계정확인
             user=Account.objects.get(Q(email=email)|Q(name=name)) # 2. 정보 가져오기
             if bcrypt.checkpw(password.encode('utf8'),user.password.encode('utf-8')) is True:	# 3. password 확인
                 payload={"id":user.id}	# 4. payload 
                 a=jwt.encode(payload,SECRET_KEY, algorithm ='HS256').decode('utf-8')	# 5. token 생성
                 return JsonResponse({"token":a},status=200)	# 6. token 보내기 
             return JsonResponse({"message":"INVALID_PASSWORD"},status=401)
         return JsonResponse({"message":"INVALID_USER"},status=401)

1) 계정확인
request.body로 들어온 정보 (email/name)이 가입이 되어있는 정보인지 확인

2) 정보 가져오기
가입이 되어있는 정보면, 해당 정보 가져와서 user라는 변수에 넣어두기

3) password 확인

bcrypt.checkpw를 이용하여 request.body로 들어온 password가 가입된 정보의 password와 동일한지 확인하기

bcrypt.checkpw의 인자는 encode된 인자여야함
password.encode('utf8') --> requeset.body로 들어온 password를 encode하기

user.password.encode('utf-8') --> user는 가입자의 정보 / 가입자의 정보의 비밀번호를 가져옴 (이 비밀번호는 암호화 후 디코드해서저장해 두었기 때문에 encode 과정 거치기)

password가 일치하면 True / 일치하지 않으면 False

4) payload
payload는 사용자 정보를 확인하는 부분
암호화하는 것이 아니므로 사용자의 정보를 담아서는 안됨

보통 회원가입 테이블의 id를 payload로 사용함

5) token 생성
a=jwt.encode(payload,SECRET_KEY, algorithm ='HS256').decode('utf-8')

jwt.encode를 이용하여 token을 만들고 만들어진 토큰을 디코드해주기

6) token 보내기
JsonResponse로 프론트엔드에게 토큰 return해주기

Decorator 만들기

생성된 토큰을 payload로 만들어주는 코드를 만들기
프론트엔드에서 request.headers의 'Authorization'에 토큰을 담아서 보냄

decorator로 만들어주어서 코드 가독성을 올리고 반복 작업을 줄일 수 있음 또 decorator를 이용하여 토큰을 payload로 만드는 것에 강제성을 부여할 수 있음

app내의 utils.py라는 새로운 파일을 만들어서 그 곳에서 decorator를 만들고 적용하기

# utils.py
import jwt

from django.http import HttpResponse,JsonResponse

from test1.settings import SECRET_KEY
from account.models import Account

def check_user(func):
    def wrapper_func(self,request,*args,**kwargs):
        access_token=request.headers.get('Authorization',None)	# 1. 토큰 가져오기

        if access_token is None:
            return JsonResponse({"message":"token plz"},status=400)
            # 2. 토큰이 없는 경우

        try:
             user_id=jwt.decode(access_token,SECRET_KEY,algorithm='HS256')	# 3. 토큰 디코드
             account=Account.objects.get(id=user_id["id"])	# 4. 정보 가져오기
             request.account=account	# 5. request에 account값 주기

        except Account.DoesNotExist:
             return JsonResponse({"message":"unknown_user"},status=401)

        except jwt.DecodeError:	# 6
            return JsonResponse({"message":"invalid_token"},status=401)

        return func(self,request,*args,**kwargs)

    return wrapper_func

1) 토큰 가져오기

request의 headers에 'Authorization'이 있으면 그 value(토큰 값)을 가져와서 access_token에 대입하기
'Authorization'이 없으면 None return하기
이때 사용하는 get은 딕셔너리 메소드

2) 토큰이 없는 경우

토큰이 없는 경우에 리턴하는 내용

3) 토큰 디코드

user_id --> 토큰

토큰을 디코드해서 payload로 만드는 과정
이 때 payload 값은 딕셔너리 형식으로 나옴

아래의 예시 참고

payload = {"id":user.id}
user_id = payload

4) 정보 가져오기

정보를 가지고 올 때 id=4와 같은 형식이어야하는데 위의 과정에서 가지고 온 payload는 딕셔너리 형태

여기서 value값만 가지고 와야함

따라서 user_id 딕셔너리의 key(id)값의 value를 가지고옴 (Account.objects.get(id=user_id["id"]) )

5) request에 account값 주기

request.account = account를 이용하여 request의 account에 위에서 가지고온 정보를 담아줌

6) 에러

유효성 검사에 실패하여 토큰을 해독 할 수 없을 때 발생
여기를 보면 더 많은 에러를 확인할 수 있음

토큰 생성의 decode와 다른점

토큰 생성 decode는 bytes로 만들어진 토큰을 str으로 바꿔주는 것

decorator의 jwt.decode는 토큰을 payload로 바꾸는 과정

Decorator 적용하기

위에서 만든 토큰을 푸는 decorator는 로그인이 필요한 환경들에 적용할 수 있음
여기서는 comments에 적용해보았음

아래와 같이 이용함

import json

 from django.views import View
 from django.http import JsonResponse,HttpResponse

 from .models import Comment
 from account.models import Account
 from account.utils import check_user	# 1. decorator import

 class CommentView(View):
     @check_user	# 2. decorator 적용 
     def post(self,request):
         try:
             data = json.loads(request.body)
             Comment(
                      email=request.account.email,	# 3. request.account
                      reply = data['reply']
                 ).save()
             return HttpResponse(status=200)
         except KeyError:
             return JsonResponse({"message":"INVALID_KEYS"},            status=400)

1) decorator import

decorator 사용하기 위해서 import해주기

2) decorator 적용

3) request.account

request.account에 account 정보를 담았음
account의 email 불러오기


ref
https://koreanblacklee.github.io/posts/decorator

0개의 댓글