django - bcrpy & jwt

김현우·2020년 8월 15일
0

django

목록 보기
3/6

오늘은 회원가입/로그인을 할 때, 암호화 및 토큰 발행 기능을 추가해보겠습니다!😃

기본적인 내용은, 인증/인가 포스팅을 참고해주세요!
여기를 클릭!!

Salting과 Key Stretching을 구현한 해쉬 함수중 가장 널리 사용되는 것이 bcrypt입니다.
bcrypt는 처음부터 비밀번호를 단방향 암호화 하기 위해 만들이전 해쉬함수입니다.

사용법에 앞서, 필요한 것들을 설치해줍니다!!

pip install bcrypt 
pip install pyjwt

필요한 것들을 import 해줍시다!

import bcrypt 
import jwt

설치가 끝나면, 사용법을 알아봅시다.


bcrypt의 암호화 방법!

bcrypt는 str 데이터가 아닌 Bytes 데이터를 암호화합니다.
따라서 암호화시에 bytes화 해야합니다!!


👊참고!!👊

파이썬에서는 str 을 encode하면 bytes(이진화) 되고, Bytes 를 decode하면 str 화 합니다.


비밀번호는 다음과같이 생성합니다.
회원가입시 만들어 줍니다.

hashed_password = bcrypt.hashpw(user_password.encode('utf-8'), bcrypt.gensalt())

database에 넣어줄때에는 decode시켜 넣어줍니다. 작성한 model 데이터의 타입을 맞추기위해서!!


            # save to database
            Account(
                name         =    user_name,
                phone_number =    user_phone_number,
                email        =    user_email,
                password     =    hashed_password.decode('utf-8')
            ).save()
            return JsonResponse({"message": "SIGN_UP_SUCCESS"},status = 200)

JWT 구현하기

암호화를 알았으니 이제 인증을 위한 수단인 JSON Web Token.

즉, JWT를 구현 해봅시다.

SECRET = 'secret' #'랜덤한 조합의 키' 예제이므로 단순하게 'secret'이라고 하겠습니다.

access_token = jwt.encode({'id' : 1}, SECRET, algorithm = 'HS256')
print(access_token)
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.-xXA0iKB4mVNvWLYFtt2xNiYkFpObF54J9lj2RwduAI'

jwt의 결과물도 bytes 타입!!

그럼 이제 인증을 위한 매개체를 만들었습니다. 바로 jwt token 일명 access_token입니다.

이렇게 발급된 토큰은 어떻게 사용할 수 있을까요? 바로 프론트엔드 엔지니어에게 전달해야 합니다.

그렇다면 로그인이 성공하면 위와 같은 방식으로 발행한 토큰을 전달하면 됩니다!


인증을 통과한 사용자만 접근하려면 토큰을 받아서 다시 우리가 발행한 토큰이 맞는지 확인해줘야합니다.

header = jwt.decode(access_token, SECRET, algorithm = 'HS256')
print(header)
{'id': 1}

decode 결과는 바로 우리가 encode 할 때 넘겼던 header값인 { 'id' : 1}입니다.
이 말은 우리가 담아 넘기는 header 값을 통해 유저를 식별할 수 있다는 이야기입니다.


bcrypt.checkpw함수를 이용하여 database의 비밀번호화 맞는지 비교해줍시다.
이때도 역시 byte타입으로 변환해주는 encoding은 필수입니다!!

pwd = Account.objects.get(name = input_name).password.encode('utf-8')
if(bcrypt.checkpw(input_password.encode('utf-8'), pwd)):
                    token = jwt.encode({'name' : input_name}, SECRET_KEY, algorithm = "HS256")
                    token = token.decode('utf-8')                          # 유니코드 문자열로 디코딩
                    return JsonResponse({"message" :"LOGIN_SUCESS", "Your_token" : token}, status=200)
                else: 
                    return JsonResponse({'message': "PASSWORD_INCORRECT"}, status = 400)

회원가입이 되었고, 올바른 비밀번호를 입력했을시 토큰을 발행해 줍니다.


전체 회원가입 로그인 코드

class SignUpView(View) : 
    def post(self,request):
        try: 
            data = json.loads(request.body)
            user_name           =    data['name']
            user_phone_number   =    data['phone_number']
            user_email          =    data['email']
            user_password       =    data['password']

            hashed_password = bcrypt.hashpw(user_password.encode('utf-8'), bcrypt.gensalt()) 
            #첫번쨰 파라미터는 암호화할 대상,

            # email validation
            if "@" not in user_email or "." not in user_email: 
                return JsonResponse({"message": "Email_ERRROR"},status = 400)

            # password validation
            if len(user_password) < 8:
                return JsonResponse({"message": "Password_ERRROR"},status = 400)   

            # overlap validation
            if Account.objects.filter(name = user_name).exists():
                return JsonResponse({"message": "Your User Name is overlapped! try again."},status = 400)
            if Account.objects.filter(phone_number = user_phone_number).exists():
                return JsonResponse({"message": "Your Phone Number is overlapped! try again."},status = 400)
            if Account.objects.filter(email = user_email).exists():
                return JsonResponse({"message": "Your Email is overlapped! try again."},status = 400) 
            
            # save to database
            Account(
                name         =    user_name,
                phone_number =    user_phone_number,
                email        =    user_email,
                password     =    hashed_password.decode('utf-8')
            ).save()
            return JsonResponse({"message": "SIGN_UP_SUCCESS"},status = 200)
            
        except KeyError:
            return JsonResponse({"message": "KEY_ERROR"}, status = 400)
        except json.decoder.JSONDecodeError:
            return JsonResponse({'message': 'JSON_TYPE_Error'}, status = 401)
        except ValueError:
            return JsonResponse({"message": "VALUSE_ERROR"}, status = 400)
    def get(self, request):
      	return JsonResponse({'Hello':'World'}, status = 200)

class SignInView(View):
    def post(self,request):
        try: 
            data = json.loads(request.body)
            input_name           =    data['name']
            input_phone_number   =    data['phone_number']
            input_email          =    data['email']
            input_password       =    data['password']

            # name validation
            if input_name == "" or input_password == "":
                return JsonResponse({"message": "KEY_ERROR"}, status = 400)
            elif Account.objects.filter(name = input_name).exists():
                pwd = Account.objects.get(name = input_name).password.encode('utf-8')
                # if(Account.objects.get(name = input_name).password == input_password):
                if(bcrypt.checkpw(input_password.encode('utf-8'), pwd)):
                    token = jwt.encode({'name' : input_name}, SECRET_KEY, algorithm = "HS256")
                    token = token.decode('utf-8')                          # 유니코드 문자열로 디코딩
                    return JsonResponse({"message" :"LOGIN_SUCESS", "Your_token" : token}, status=200)
                else: 
                    return JsonResponse({'message': "PASSWORD_INCORRECT"}, status = 400)
            else:
                return JsonResponse({"message": "INVALID_USER"}, status = 401) 
        except KeyError :
            return JsonResponse({"message": "KEY_ERROR"}, status = 400)
        except json.decoder.JSONDecodeError:
            return JsonResponse({'message': 'JSON_TYPE_Error'}, status = 401)
        except ValueError:
            return JsonResponse({"message": "VALUSE_ERROR"}, status = 400)
            
    def get(self, request):
        signin_data = Account.objects.values()
        return JsonResponse({'message': list(signin_data)}, status = 200)
profile
코딩을 잘하는 개발자가 되자!

0개의 댓글