인증 & 인가
인증(Authentication)
Authentication은 유저의 identification을 확인하는 절차
일반적 절차
- 유저 아이디와 비번 생성 -> 유저 비번 암호화 해서 DB에 저장 > 유저 로그인 -> 아이디와 비밀번호 입력 > 유저가 입력한 비밀번호 암호화 한후 암호화되서 DB에 저정된 유저 비밀번호와 비교 -> 일치하면 로그인 성공 > 로그인 성공하면
access token
을 클라이언트에게 전송 -> 유저는 로그인 성공후 다음부터는access token
을 첨부해서 request를 서버에 전송함으로서 매번 로그인 해도 되지 않도록 한다.
Login View 파일에 작성
토큰 발행 & 암호화
실습 준비
- 라이브러리 설치
bcrypt
, JWT
라이브러리를 설치한다.
> pip install bcrypt
> pip install pyjwt
- import
python manage.py shell
> import bcrypt
- bcrypt 암호화 방법
bcrypt를 사용하게 되면 str 데이터가 아닌 Bytes 데이터로 암호화가 진행됨. 꼭 Bytes로 변환을 해줘야함. 파이썬에서는 str
을 encode하면 bytes(이진화) 되고, Bytes
를 decode하면 str
화 할 수 있음. encode, decode시에는 우리가 인식할 수 있는 형태로 변환하기 위해 'UTF-8' 유니코드 문자 규격을 사용
- bcrpyt.hashpw() - 오류 확인하기
>>> bcrypt.hashpw()
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: hashpw() missing 2 required positional arguments: 'password' and 'salt'
>>>
오류 확인. 2개의 인자를 필요로 하는데, password와 salt를 필요로한다.
- 먼저 소금을 생성하기
>>> salt = bcrypt.gensalt()
>>> print(salt)
b'$2b$12$fuDkhOEKVo7BVpl8SXW7aO'
>>>
이런 식으로 소금값?을 구해줄 수 있다. 아직 암호는 건드리지 않았다.
- password 설정
>>> bcrypt.hashpw('12341234', salt)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/dongheon/miniconda3/envs/CRUD2/lib/python3.8/site-packages/bcrypt/__init__.py", line 80, in hashpw
raise TypeError("Unicode-objects must be encoded before hashing")
TypeError: Unicode-objects must be encoded before hashing
다시 오류 발생. 잃어 보면 Unicode-objects must be encoded before hashing
이라는 뜻은 일단 해싱을 하려면 인코드를 해야한다는 뜻.
>>> password = '12341234'.encode('utf-8')
>>> print(password)
b'12341234'
>>> type(password)
<class 'bytes'>
>>>
타입도 바뀌어 있고, 프린트로 출력을 해보면 앞에 b가 붙어 bytes 형태로 인코드 되어 있는 것을 확인할 수 있음
- 정상 작동 확인
>>> hashed_password = bcrypt.hashpw(password, salt)
>>> print(hashed_password)
b'$2b$12$fuDkhOEKVo7BVpl8SXW7aOB/JBLjHHUCnT8MAXFFbbXjPjqrz6fZK'
- checkpw
>>> bcrypt.checkpw(password, hashed_password)
True
checkpw 통헤 확인
이렇게 암호화된 방식은 일방향 암호화 즉 복호화 할 수 없도록 암호화하는 방식
- Decode('utf-8')
else:
User.objects.create(
email = data['email'],
name = data['name'],
phoneNumber = data['phoneNumber'],
password = hashed_pw.decode('utf-8')
회원가입 views.py 작성 시 주의 사항.
password 부분을 hashed_pw
로 바꿔주고 나서, .decode('utf-8')
형태로 다시 작성해 줘야 한다. .decode('utf-8')
로 그대로 나둘 시 바이트 형식으로 계속 남아 있게 되어 로그인 시 비교를 할 수 없게 된다.
코드 리뷰 & 토큰 발행
import bcrypt
import jwt
import json
from django.http import JsonResponse
from django.views import View
from .models import User
class UserView(View):
def post(self, request):
data = json.loads(request.body)
PASSWORD_LENGTH = 8
email = data['email']
name = data['name']
phoneNumber = data['phoneNumber']
password = data['password']
encoded_pw = password.encode('utf-8')
hashed_pw = bcrypt.hashpw(encoded_pw, bcrypt.gensalt())
if not (email or password):
return JsonResponse({"message" : "KEY_ERROR."}, status = 400)
try:
if User.objects.filter(email = data['email']).exists() == True:
return JsonResponse({"message" : "같은 아이디가 존재합니다."}, status = 401)
except:
pass
if ('@' and '.') not in email:
return JsonResponse({"message" : "아이디를 제대로 써주세요"}, status = 401)
if len(password) < PASSWORD_LENGTH:
return JsonResponse({"message" : "비밀번호를 8글자 이상으로 늘려주세요"}, status = 401)
else:
User.objects.create(
email = data['email'],
name = data['name'],
phoneNumber = data['phoneNumber'],
password = hashed_pw.decode('utf-8')
)
return JsonResponse({"message" : "Success"}, status = 200)
class SignInView(View):
def post(self, request):
data = json.loads(request.body)
email = data['email']
password = data['password']
try:
if User.objects.filter(email=data['email']).exists():
user_email = User.objects.get(email=data['email'])
if bcrypt.checkpw(data['password'].encode('utf-8'),user_email.password.encode('utf-8')):
access_token = jwt.encode({'id': user_email.id}, 'secret', algorithm='HS256')
return JsonResponse({"token": access_token, "message": "SUCCESS"}, status=200)
else:
return JsonResponse({"message": "INVALID_PASSWORD"}, status=401)
else:
return JsonResponse({"message": "INVALID_EMAIL"}, status=401)
except KeyError:
return JsonResponse({"message": "KEY_ERROR"}, status=400)
중요하다고 생각하는 부분은 짚고 넘어가기!
try ~ except 구문
try:
if User.objects.filter(email = data['email']).exists() == True:
return JsonResponse({"message" : "같은 아이디가 존재합니다."}, status = 401)
except:
pass
try ~ except 구문을 잘 이해하지 못해 코드 중간에 껴 넣은 형태로 짜버렸음. 추후 다시 고쳐야 하는 부분.
password 부분 상수 처리
PASSWORD_LENGTH = 8
if len(password) < PASSWORD_LENGTH:
return JsonResponse({"message" : "비밀번호를 8글자 이상으로 늘려주세요"}, status = 401)
처음에는 if 안에 직접적으로 숫자를 넣어 일정 글자 수 이상을 조건으로 주었었음. 하지만 그렇게 하면 기획 시 제시하는 기준이 바뀌게 되었을 때 다시 찾아서 글자 수를 지정해줘야함. 이렇게 바뀔 수 있는 부분은 따로 변수를 만들어 관리를 해주면 좋을 것.
토큰 관련
if bcrypt.checkpw(data['password'].encode('utf-8'),user_email.password.encode('utf-8')):
access_token = jwt.encode({'id': user_email.id}, 'secret', algorithm='HS256')
access_token = jwt.encode({'id': user_email.id}, 'secret', algorithm='HS256')
access_token을 발행을 할건데, 일단 jwt라는 라이브러리?를 사용하여 진행함. 인자로 3개가 필요한데, 첫 번째는 유저의 아이디 값을 id라는 키에 저장을 해야하고, secret 키를 적어주고, 마지막엔 어떤 알고리즘으로 인코드할 것인가에 대해 적어줌.
Author And Source
이 문제에 관하여(인증 & 인가), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@dhhyy/인증-인가저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)