Westagram #3

chaerin·2021년 2월 8일
0

Westagram

목록 보기
3/5

로그인 엔드포인트

로그인 뷰 작성

  • 인스타그램에 회원가입 할 때에는 사용자 계정(예:전화번호, 사용자 이름 또는 이메일 중 하나)을 필수로 필요합니다.
  • 인스타그램에 로그인 할 때에는 비밀번호가 필수로 필요합니다.
  • 계정이나 패스워드 키가 전달되지 않았을 시, {"message": "KEY_ERROR"}, status code 400 을 반환합니다.
  • 계정이 존재하지 않을 때나 비밀번호가 맞지 않을 때, {"message": "INVALID_USER"}, status code 401을 반환합니다.
  • 로그인이 성공하면 {"message": "SUCCESS"}, status code 200을 반환합니다.

로그인 클래스 생성

class UserSignInView(View):		# View 클래스를 상속하는 UserSignUpView 클래스 생성
  def post(self, request):		# post 메소드 사용

로그인(login)시 get 메소드 vs post 메소드 ?
처음 로그인 뷰를 작성할 때 get 메소드를 사용해야 할지 post 메소드를 사용해야 할지 고민이 되었다. 정보를 가져와야 하기 때문에 당연히(?) get 메소드를 사용해야 한다고 생각했는데 get 메소드를 사용하게 되면 url에 정보를 담아 요청을 보내야 하고 그렇게 되면 보안이 중요시 되는 로그인 요청 방식으로 적합하지 않다는 생각이 들었다.

구글링해 본 결과 역시나 보안의 이유로 request body에 정보를 담아 요청하는 post 메소드가 적합하다는 것을 알 수 있었다.
https://stackoverflow.com/questions/43965316/for-login-get-or-post

에러 발생 상황 1

요청받은 이메일 주소가 데이터에 존재하는지 확인한다.

이메일 주소가 존재하는 경우
1️⃣ 로그인 시도한 사용자의 정보로 해당 이메일주소가 저장된 데이터를 저장해주고
2️⃣ 로그인에 필요한 비밀번호 또한 맞는지 확인하는 절차를 거치게 된다.

존재하지 않을 시 에러를 반환한다.

if Account.objects.filter(email=email).exists():	# 저장된 이메일 주소가 존재하는 경우
  login_user = Account.objects.get(email=email)		# login_user에 해당 user 데이터 저장
  .
  .
  .
return JsonResponse({'MESSAGE': 'INVALID_USER'}, status=401)

에러 발생 상황 2

존재하는 이메일 주소(사용자)임을 확인한 후 비밀번호가 맞는지 확인한다. 이 과정에서 암호화를 위한 bcrypt을 만나게 된다.

먼저 bcrypt를 설치해주고 import도 해주었다.

pip install bcrypt	# 설치

import bcrypt		# import하기

bcrypt의 암호화 방법
bcrypt는 str데이터가 아닌 bytes데이터를 암호화한다. 따라서 암호화할 때 bytes화 해주어야 한다.

파이썬에서는
encode : str -> bytes
decode : bytes-> str

encode, decode시에는 우리가 인식할 수 있는 형태로 변환하기 위해 'utf-8'유니코드 문자 규격을 사용한다.

db에 비밀번호가 암호화된 형태로 저장이 되야하므로 회원가입시 password를 아래와 같이 create 해준다.

password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')

db에 비밀번호를 저장할 때 encode해준 비밀번호를 다시 decode해주었는데 이런 과정을 거치면

encode() -> b'$2b$12$cnQdYm24fHIfbcL1pJRTBOKx6HN1QFZ7QiX6sb08VAjDZEWYMuz0G'로 저장되었던 것이

encode().decode() -> $2b$12$cnQdYm24fHIfbcL1pJRTBOKx6HN1QFZ7QiX6sb08VAjDZEWYMuz0G로 저장되는 것을 확인할 수 있었다.

if  bcrypt.checkpw(password.encode('utf-8'), login_user.password.encode('utf-8')):
# 요청받은 패스워드를 암호화한 것과 db에 저장되어 있던 패스워드 비교

  access_token=jwt.encode({'id' : login_user.id}, 'secret', algorithm='HS256')
  # 동일하다면 토큰 발행
  
  return JsonResponse({'MESSAGE': 'SUCCESS', 'ACCESS_TOKEN': access_token}, status=200)
  # 성공 메시지, 토큰 정보, 코드 반환

return JsonResponse({'MESSAGE': 'INVALID_PASSWORD', status=401)
# 동일하지 않을 때 에러메시지 및 코드 반환

내가 접한 에러들

첫번째. TypeError: Unicode-objects must be encoded before checking

db에 저장하는 패스워드를 암호화 하는 과정을 아래와 같이 작성했다.

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

문제없이 들어갔다고 생각했고 로그인을 시도했다.

bcrypt.checkpw(password.encode('utf-8'), login_user.password)

.
.
.

에러 해결 방법을 찾다가 stackoverflow에서 같은 문제를 맞이한 글을 발견했고 아래의 방법 제일 간단한 방법같아서 적용해보았다.

https://stackoverflow.com/questions/40577867/bcrypt-checkpw-returns-typeerror-unicode-objects-must-be-encoded-before-checkin

그리고는 아래의 에러를 맞이하게 되었다.

두번째. ValueError : Invalid salt

위에서 db에 패스워드를 저장할 때 encode한 것을 decode 해준 이유가 바로 이 에러를 맞이했기 때문이다.

decode 해준 뒤의 패스워드 자료형을 확인해보면 str형이라서 checkpw할 때 다시 encode해주는 것인가........? 싶다.

✅ bcrypt.checkpw(입력받은 패스워드, 저장된 암호화된 패스워드) 메소드의 규칙
==> 둘 다 데이터 타입이 Bytes 여야 한다.

완료

+ urls.py 수정


로그인을 위한 UserSignInView와 회원가입을 위한 UserSignUpView를 import해준다.

urlpatterns에는 어떤 작업을 할 것인지, 어떤 클래스로 접근할 것인지 명확히 하기 위해서 경로를 지정해 준다.

회원가입
http POST 127.0.0.1:8000/user/SignUp email='이메일주소' password='패스워드'

로그인
http POST 127.0.0.1:8000/user/SignIn email='이메일주소' password='패스워드'

as_view()
as_view()는 UserSignUpView 클래스, UserSignInView 클래스 모두가 가지고 있는 메소드이다.

그렇다면 이 두 클래스는 어떻게 as_view라는 메소드를 가지고 있는 것일까?


views.py에 import 해준 View에서 상속 받은 것이다.

🔆 as_view()역할 => get 요청이 오면 get함수를, post 요청이 오면 post 함수를 실행시킨다.

0개의 댓글