[ PROJECT ] Westagram endpoint 구현 (3) - bcrypt로 암호화 & jwt로 access_token 발행 & UI에서 확인

Hailee·2020년 12월 11일
0

[ PROJECT ]

목록 보기
3/16
post-thumbnail

앞서 세션 시간에 배운 인증/인가는 프론트 - 백 간의 상호작용!
오늘 그 과정을 모두 진행해본 뒤 ui에서 직접 백엔드 서버와 통신하는 것까지 모두 실습해보기로 한다!


bcrypt로 암호화 시작하기

지금 하는 작업은 전체적인 인증/인가가 아닌 백엔드 엔지니어가 인증/인가를 구현하기 위한 것 🙌🏻

우선 bcrypt , JWT 라이브러리를 설치한다.

> pip install bcrypt
> pip install pyjwt

bcrypt는 인증 구현에 앞서, 개인정보의보호를 위해 필수적으로 해야하는 요소인 비밀번호 암호화를 할 수 있게 해주는 라이브러리!

python 인터프리터를 실행한 후 설치한 라이브러리를 import 해주기!

> import bcrypt

bcrypt의 암호화 방법

bcryptstr 데이터가 아닌, bytes 데이터를 암호화한다.
👉🏻 암호화 시에 bytes화 해야 한다!

  • 파이썬에서는 strencode하면 bytes(이진화) 되고, Bytes 를 decode하면 str 화 합니다.
  • encode, decode시에는 우리가 인식할 수 있는 형태로 변환하기 위해 'UTF-8' 유니코드 문자 규격을 사용합니다.


👆🏻 실습하기!
password라는 변수를 단방향 암호화해보기!(복호화 할 수 없도록...!)
비밀번호를 생성하는 방법은 : hashpw( ) 메서드 사용하기!
비밀번호를 확인하는 방법은 : checkpw( )메서드 사용하기..!

checkpw( ) 메서드

  • 입력받은 패스워드, 저장된 암호화된 패스워드
    둘 다 데이터 타입이 bytes여야 한다.

이제 dJango에 적용해보기!

import json
import re
import bcrypt

from django.http        import JsonResponse
from django.views       import View
from user.models        import User

# sign up
class Signup(View):
    def post(self, request):
        try:
            data = json.loads(request.body)
            if data.get('user_name') is not None:
                if data.get('password') is not None:
                    if User.objects.filter(user_name=data['user_name']).count() > 0:
                        return JsonResponse({'message':'DUPLICATED_USERNAME'}, status=400)
                    if re.search('[0-9]+', data['password']) is None:
                        return JsonResponse({'message':'TOO_SHORT_PW'}, status=400)
                    
                    hashed_password = bcrypt.hashpw(data['password'].encode('utf-8'), bcrypt.gensalt())
                    User.objects.create(
                        user_name   = data['user_name'],
                        # password    = data['password']
                        password    = hashed_password.decode('utf-8')
                    )
                    return JsonResponse({'message':'SUCCESS'}, status=200)
                else:
                    return JsonResponse({'message':'KEY_ERROR'}, status=400)
            else:
                return JsonResponse({'message':'KEY_ERROR'}, status=400)
        except:
            return JsonResponse({'message':'KEY_ERROR'}, status=400)

# sign in
class Signin(View):
    def post(self, request):
        data = json.loads(request.body)
        try:
            user_count = User.objects.filter(user_name=data['user_name']).count()
            signin_id **텍스트**= User.objects.get(user_name=data['user_name']).user_name
            signin_pw = User.objects.get(user_name=data['user_name']).password.encode('utf-8')
            
            print(bcrypt.checkpw(data['password'].encode('utf-8'), signin_pw))

            if user_count == 1:
                #if signin_id == data['user_name'] and signin_pw == data['password']:
                if signin_id == data['user_name'] and bcrypt.checkpw(data['password'].encode('utf-8'), signin_pw):
                    return JsonResponse({'message':'SUCCESS'}, status=200)
            else:
                return JsonResponse({'message':'INVALID_USER'}, status=401)    
        except:
            return JsonResponse({'message':'INVALID_USER'}, status=401)

하하하핳 세상은 예제와많이 다르더라
아무튼 어찌저찌 끝낸 bcrypt로 암호화해서 DB에 저장(회원가입)하고, 저장된 값 불러와서 로그인할 때 비교해보기!


JWT로 access_token 생성하기

인증을 위한 수단인 JSON Web Token, JWT 구현하기!
앞서 이미 install을 했기 때문에, 바로 python 인터프리터에서 실습 고고

패키지명은 pyjwt이지만, import 할 때에는 jwt로 할 것!

jwt의 결과물도 bytes 타입!
아무튼 우리는 이 jwt를 이용해서 jwt token, access_token을 생성해야 한다!
이 토큰이 프론트엔드에서 로그인의 성공 여부를 알 수 있게 해주는 것!

로그인 단계에서 인증을 끝낸 사람이라면(jwt.encode), 토큰을 받아서 백엔드에서 발행한 토큰이 맞는지 확인해준다! (jwt.decode)

👉🏻 decode결과는 우리가 encode할 때 넘겼던 header값이다

  • 우리가 담아 넘기는 header 값을 통해 유저 식별이 가능하다
  • 인증 코드는 보통 엔드포인트에 데코레이터를 구현하며,
    데코레이터 구현은 보통 applicationutils.py에 작성!

인증&인가 그리고 access_token!

  • 예제에서 배운 access_token 발행은 utils.py내에 작성한 것을 decorator를 사용해서 메서드 실행 이전에 사용하는 것!이라고 나와있다.
    하지만 암만 생각해도, 이해가 안 가는 것이 아닌가 ㅠㅠㅠㅠ
    결국 못참고 밤새도록 내가 짠 코드를 들쑤시다가, 이러다간 정말 큰일낼 것 같아서
    아침이 되자마자 위코드 커뮤니티에 질문글을 썼다.
    (구글에 나온 예제들을 보면 다들 너무 쉽게 작성한 것 같던데.. 이것도 모르는 내가 너무 바보같고.. 흑흑)

    그리고 그에 달린 답변은? 👇🏻

    결국 언제나 그랬던 것처럼 이해가 부족해서! 이해가 부족한데 무작정 구현하려고 해서 그랬던 것🤯 🥺
    결국 권한을 어느 시점에 부여하고, 어느 시점에서 권한을 체크하는가 이것이 중요했던 것!
    그래서 내가 내린 결론은...

  • 로그인 할 때에는 access_token만 발행해서 리턴하는것이 맞고,
  • 이후 권한 체크가 필요한 작업에 따라서 메서드별로 decorator를 적용하는 것!
    ex ) 로그인 후 메인화면으로 리턴한다 = access_token 만 보낸다
    ex ) 게시판 접속을 원한다 = 게시판 접속 메서드 실행 시 decorator 적용해서 decode하여 권한 비교한다

그렇게 고생 끝에 내가 작성한 코드는..👇🏻

import json
import re
import bcrypt
import jwt

from django.http        import JsonResponse
from django.views       import View
from user.models        import User
from westagram.settings     import SECRET_KEY, ALGORITHM
# sign up
class Signup(View):
    def post(self, request):
        try:
            data = json.loads(request.body)
            if data.get('user_name') is not None:
                if data.get('password') is not None:
                    if User.objects.filter(user_name=data['user_name']).count() > 0:
                        return JsonResponse({'message':'DUPLICATED_USERNAME'}, status=400)
                    if re.search('[0-9]+', data['password']) is None:
                        return JsonResponse({'message':'TOO_SHORT_PW'}, status=400)
                    
                    hashed_password = bcrypt.hashpw(data['password'].encode('utf-8'), bcrypt.gensalt())
                    User.objects.create(
                        user_name   = data['user_name'],
                        # password    = data['password']
                        password    = hashed_password.decode('utf-8')
                    )
                    return JsonResponse({'message':'SUCCESS'}, status=200)
                else:
                    return JsonResponse({'message':'KEY_ERROR'}, status=400)
            else:
                return JsonResponse({'message':'KEY_ERROR'}, status=400)
        except:
            return JsonResponse({'message':'KEY_ERROR'}, status=400)

# sign in
class Signin(View):
    def post(self, request):
        try:
            data = json.loads(request.body)
            user_count = User.objects.filter(user_name=data['user_name']).count()
            signin_id = User.objects.get(user_name=data['user_name']).user_name
            signin_pw = User.objects.get(user_name=data['user_name']).password.encode('utf-8')
            
            if user_count == 1:
                #if signin_id == data['user_name'] and signin_pw == data['password']:
                if signin_id == data['user_name'] and bcrypt.checkpw(data['password'].encode('utf-8'), signin_pw):
                    access_token = jwt.encode({'user_name':data['user_name']}, SECRET_KEY, algorithm = ALGORITHM).decode('utf-8')
                    return JsonResponse({'message':'SUCCESS', 'access_token':access_token}, status=200)
            else:
                return JsonResponse({'message':'INVALID_USERd'}, status=401)    
        except:
            return JsonResponse({'message':'INVALID_USERs'}, status=401)

httpie로 테스트해보면 요로케 아름답게 내가 정해준 것들을 리턴하고 있는 것을 볼 수 있다><
드디어 보는구나 access_token 요 앙큼한것 🥺


UI로 진짜진짜 테스트하기!

>> 서버 외부 접속 허용

1) Settings.py 수정

> ALLOWED_HOSTS = ['*']

allowed host에 *을 적어주는 이유?
: 유동 ip를 쓰는 작업 환경이기 때문에, 프론트에서도 내 백엔드 서버에 접속할 수 있도록 해주어야 하므로!

2) 서버 구동할 때

> python manage.py runserver 0.0.0.0:8000 or 0:8000

기존에 입력하던 명령어 python manage.py runserver는 내 기본 포트의 서버를 start 해주기 때문에,
특정 번호의 서버를 start 해주어야한다!

공유해야 할 사항

1) 내 ip 주소

각 개발환경의 터미널 창에서 다음 명령어를 입력하고 내 ip주소를 확인한다.

 # Linux
> hostname -I
 # MAC
> ipconfig getifaddr en0

2) 엔드포인트 url 주소 : Request Target

프론트에서 내가 만든 메서드의 url(urls.py에 적어둔 기능별 경로들!)을 통해
기능을 사용할 수 있도록 해주는 것!

 # 회원가입 (예시)
> 10.58.1.40:8000/user/signup
 # 로그인 (예시)
> 10.58.1.40:8000/user/signin

내가 사용하고자 하는 함수는 서버 컴퓨터에 있으므로,
클라이언트의 컴퓨터에서 ip를 통해 해당하는 컴퓨터의 서버에 접속해서
이전에 함수 하나당 설정해준 url(request target)을 타고 들어가서 기능을 찾는다!

아무튼, html파일 상에서 내 로컬 컴퓨터 서버로 접속해서 직접 DB와 소통해본 결과,
내가 지정한 대로 잘 출력력이 되는 것을 확인할 수 있다!!

드디어 대략적인 로그인, 회원가입 기능 구현이 끝났다!!!! 와아 징글><
주말동안에.. advanced한 파이썬 사용 기술을 공부할 것인가, 아니면 django 웹개발 기술들을 더 공부할 것인가..!!!!!!
(decorator도 할 줄 모르니까 답은 전자+후자로 정해져있지..!! 후후)

profile
웹 개발 🐷😎👊🏻🔥

0개의 댓글