앞서 세션 시간에 배운 인증/인가는 프론트 - 백 간의 상호작용!
오늘 그 과정을 모두 진행해본 뒤 ui에서 직접 백엔드 서버와 통신하는 것까지 모두 실습해보기로 한다!
우선 bcrypt
, JWT
라이브러리를 설치한다.
> pip install bcrypt
> pip install pyjwt
bcrypt
는 인증 구현에 앞서, 개인정보의보호를 위해 필수적으로 해야하는 요소인 비밀번호 암호화를 할 수 있게 해주는 라이브러리!
python
인터프리터를 실행한 후 설치한 라이브러리를 import 해주기!
> import bcrypt
bcrypt의 암호화 방법
bcrypt
는str
데이터가 아닌,bytes
데이터를 암호화한다.
👉🏻 암호화 시에 bytes화 해야 한다!
- 파이썬에서는
str
을 encode하면 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에 저장(회원가입)하고, 저장된 값 불러와서 로그인할 때 비교해보기!
인증을 위한 수단인 JSON Web Token, JWT 구현하기!
앞서 이미 install을 했기 때문에, 바로 python 인터프리터에서 실습 고고
패키지명은 pyjwt
이지만, import 할 때에는 jwt
로 할 것!
jwt의 결과물도 bytes
타입!
아무튼 우리는 이 jwt를 이용해서 jwt token, access_token
을 생성해야 한다!
이 토큰이 프론트엔드에서 로그인의 성공 여부를 알 수 있게 해주는 것!
로그인 단계에서 인증을 끝낸 사람이라면(jwt.encode
), 토큰을 받아서 백엔드에서 발행한 토큰이 맞는지 확인해준다! (jwt.decode
)
👉🏻
decode
결과는 우리가encode
할 때 넘겼던header
값이다
- 우리가 담아 넘기는 header 값을 통해 유저 식별이 가능하다
- 인증 코드는 보통 엔드포인트에 데코레이터를 구현하며,
데코레이터 구현은 보통application
내utils.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
요 앙큼한것 🥺
> ALLOWED_HOSTS = ['*']
allowed host
에 *을 적어주는 이유?
: 유동 ip를 쓰는 작업 환경이기 때문에, 프론트에서도 내 백엔드 서버에 접속할 수 있도록 해주어야 하므로!
> python manage.py runserver 0.0.0.0:8000 or 0:8000
기존에 입력하던 명령어 python manage.py runserver
는 내 기본 포트의 서버를 start 해주기 때문에,
특정 번호의 서버를 start 해주어야한다!
각 개발환경의 터미널 창에서 다음 명령어를 입력하고 내 ip주소를 확인한다.
# Linux
> hostname -I
# MAC
> ipconfig getifaddr en0
프론트에서 내가 만든 메서드의 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
도 할 줄 모르니까 답은 전자+후자로 정해져있지..!! 후후)