반응형
merge된 main git pull로 update후 새로운 브랜치를 내서 작업
로그인 로직 변경
회원가입 할 때 비밀번호를 암호화해서 저장했기 때문에 로그인 할때 입력된 비밀번호를 똑같이 암호화 해서 암호화된 비밀번호와 비교하는 작업을 해야 한다.
비밀번호 비교는 bcrypt의 checkpw 메서드를 이용할거라서 기존에 filter로 이메일, 비밀번호를 동시에 비교했던 코드를 수정해야 한다.
- 회원이 아닐때 로직 변경
# 기존 코드
# try:
# if not User.objects.filter(email=email, password=password).exists():
# return JsonResponse({"message": "INVALID_USER"}, status=401)
# 변경
# email과 password를 각각 따로 판단해야 해서 user에 로그인 정보로 들어온 email과 email이 일치하는 유저를 user변수에 넣었다.
# 그리고 그런 user가 존재하지 않을 경우(회원이 아닐 경우) 에러 반환
try:
...
user = User.objects.get(email=email)
...
except User.DoesNotExist:
return JsonResponse({"message": "INVALID_USER"}, status=401)
- 비밀번호가 맞지 않을때 로직 변경
...
user = User.objects.get(email=email)
if not bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
return JsonResponse({"message": "INVALID_USER"}, status=401)
...
# bcrypt의 checkpw메소드도 바이트 형식의 인자를 받기 때문에 입력된 패스워드와 user의 패스워드를 모두 인코딩 해서 비교한다.
# 첫번째 인자는 입력된 패스워드, 두번째 인자는 암호화된 패스워드이다.
# bcrypt를 이용해 암호화된 비밀번호는 솔트값과 알고리즘 키스트레칭정보가 모두 들어있어서 따로 솔트값을 저장해주지 않아도 된다
# checkpw 메서드를 통해 비밀번호 비교도 쉽게 가능하다.
>테스트
더보기
비밀번호 불일치
http -v POST http://127.0.0.1:8000/user/signin email='user1@gmail.com' password='user5pw!'
POST /user/signin HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 52
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/3.2.1
{
"email": "user1@gmail.com",
"password": "user5pw!"
}
HTTP/1.1 401 Unauthorized
Content-Length: 27
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Sat, 16 Jul 2022 16:48:27 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.12
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"message": "INVALID_USER"
}
존재하지 않는 유저
http -v POST http://127.0.0.1:8000/user/signin email='user@gmail.com' password='user5pw!'
POST /user/signin HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 51
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/3.2.1
{
"email": "user@gmail.com",
"password": "user5pw!"
}
HTTP/1.1 401 Unauthorized
Content-Length: 27
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Sat, 16 Jul 2022 16:55:39 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.12
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"message": "INVALID_USER"
}
성공
http -v POST http://127.0.0.1:8000/user/signin email='user5@gmail.com' password='user5pw!'
POST /user/signin HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 52
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/3.2.1
{
"email": "user5@gmail.com",
"password": "user5pw!"
}
HTTP/1.1 200 OK
Content-Length: 22
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Sat, 16 Jul 2022 16:48:20 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.12
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"message": "SUCCESS"
}
JWT발급
로그인 성공시 토큰을 발급해서 로그인에 성공한 유저의 다음 요청때 토큰과 함께 요청을 주면 다시 로그인을 하지 않아도 되도록 한다.
- jwt 설치, import
pip install pyjwt
# views.py
import json, re, bcrypt, jwt
- 로그인 성공시 JWT의 payload에 로그인 한 user의 id를 담아서 발급해 주세요.
jwt토큰은 encode메서드로 생성한다.
jwt.encode({페이로드:에 담을 내용}, '시크릿키', '알고리즘')
payload에 user의 id를 담아서 발급하려면 아래처럼 하면 된다.
jwt.encode({'user_id':user.id}, '시크릿키', algorithm='알고리즘')
jwt.encode({'user_id':user.id}, '시크릿키', '알고리즘') # algorithm= 은 없어도 된다
여기서 시크릿 키는 초기세팅에서 my_settings.py에 옮겨놓았던 SECRET_KEY를 사용하면 된다.
이 키와 알고리즘으로 페이로드에 담긴 정보를 확인할 수 있기 때문에 알고리즘 방법과 시크릿 키는 노출되면 안된다.
그래서 my_settings.py에 알고리즘 정보도 저장을 하고, vews.py에서 import하는 방법을 사용할 것이다.
# my_settings.py
DATABASES = {
'default' : {
...
}
}
SECRET_KEY = 'settings.py에 있던 키'
ALGORITHM = '알고리즘방법'
# settings.py
...
from my_settings import SECRET_KEY, DATABASES, ALGORITHM
...
ALGORITHM = ALGORITHM # 추가
...
# users.views.py
...
from django.conf import settings # 추가
# settings.py의 SECRET_KEY와 ALGORITHM을 사용하려면 settings.SECRET_KEY, settings.ALGORITHM을 사용하면 된다.
# django.conf의 settings는 프로젝트의 settings.py를 가리킨다.
# from my_settings import SECRET_KEY, ALGORITHM을 하지 않는 이유는 views.py의 my_settings.py의존성을 없애기 위해서다.
# settings.py에서 환경변수를 다른식으로 관리하면 views.py에 있는 my_settings.py관련 코드를 전부 수정해야한다.
# 그래서 settings.py에서 직접 가져오는 것이 좋다.
...
access_token = jwt.encode({'user_id':user.id}, settings.SECRET_KEY, settings.ALGORITHM)
...
- 발급한 JWT를 응답의 데이터로 반환해 주세요.
# 로그인 성공시 리턴값을 이렇게 변경해 준다.
# jwt버전이 바뀌어서 인코딩된 jwt가 문자열 타입이기 때문에 그대로 반환해주면 된다
return JsonResponse({"message": "SUCCESS", 'token': access_token}, status=200)
> 완성된 views.py
더보기
import json, re, bcrypt, jwt
from django.http import JsonResponse
from django.views import View
from django.conf import settings
from users.models import User
class SignUpView(View):
def post(self, request):
try:
data = json.loads(request.body)
name = data['name']
email = data['email']
password = data['password']
mobile_number = data['mobile_number']
REGEX_EMAIL = '^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$'
REGEX_PASSWORD = '^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$'
if not re.match(REGEX_EMAIL, email):
return JsonResponse({'message':'EMAIL_VALIDATION_FALSE'}, status=400)
if not re.match(REGEX_PASSWORD, password):
return JsonResponse({'message':'PASSWORD_VALIDATION_FALSE'}, status=400)
if User.objects.filter(email=email).exists():
return JsonResponse({'message': 'EMAIL_MUST_BE_UNIQUE'}, status=400)
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
User.objects.create(
name = name,
email = email,
password = hashed_password,
mobile_number = mobile_number
)
return JsonResponse({'message': 'SUCCESS'}, status=201)
except KeyError:
return JsonResponse({"message": "KEY_ERROR"}, status=400)
class SignInView(View):
def post(self, request):
try:
data = json.loads(request.body)
email = data['email']
password = data['password']
user = User.objects.get(email=email)
if not bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
return JsonResponse({"message": "INVALID_USER"}, status=401)
access_token = jwt.encode({'user_id':user.id}, settings.SECRET_KEY, settings.ALGORITHM)
return JsonResponse({"message": "SUCCESS", 'token': access_token}, status=200)
except KeyError:
return JsonResponse({"message": "KEY_ERROR"}, status=400)
except User.DoesNotExist:
return JsonResponse({"message": "INVALID_USER"}, status=401)
> 테스트
더보기
http -v POST http://127.0.0.1:8000/user/signin email='user5@gmail.com' password='user5pw!'
POST /user/signin HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 52
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/3.2.1
{
"email": "user5@gmail.com",
"password": "user5pw!"
}
HTTP/1.1 200 OK
Content-Length: 134
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Sat, 16 Jul 2022 17:46:05 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.12
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"message": "SUCCESS",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo2fQ.RJsvU12QGK1Gjo_hoHygWCeek6MyOtBZtuyG9Ks4SYs"
}
-> 깃허브에 push후 pr
반응형
'wecode' 카테고리의 다른 글
구방문방구 | 전체/카테고리별 상품리스트 api (0) | 2022.07.30 |
---|---|
구방문방구 | 데이터베이스 모델링 (0) | 2022.07.24 |
[Mission 5] 회원가입 비밀번호 암호화 적용 (0) | 2022.07.17 |
[Mission 4] 로그인 기능 구현 (0) | 2022.07.16 |
[Mission 3] 회원가입 기능 구현 (0) | 2022.07.16 |