[210726 TIL] Django
해시 (Hash)
>>> encoded_password = password.encode('utf-8') >>> encoded_password b'1234'
>>> type(encoded_password) <class 'bytes'>
>>> type(password) <class 'str'>
- 인코딩한 str타입은 bytes 타입으로 바뀐다.
>>> decoded_password = encoded_password.decode('utf-8') >>> decoded_password '1234' >>> type(decoded_password) <class 'str'>
- 디코딩된 bytes 타입은 다시 str 타입으로 바뀐다.
>>> hashed_password = bcrypt, hashpw( password, bcrypt.gensalt() ) Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'hashpw' is not defined
- 인코딩 되지 않은 password를 hash하면 에러가 발생한다.
>>> hashed_password = bcrypt.hashpw( password.encode('utf-8'), bcrypt.gensalt() ) >>> hashed_password b'$2b$12$JNrMFFk4GJxBlPhRlBgO1.KqxX7wUQtQNRNIXnrqtrk7FVUX/2h5K'
- 인코딩한 password를 hash라면 패스워드가 잘 해시된 것을 볼 수 있다.
>>> bcrypt.gensalt() b'$2b$12$RIMTPk4mPJyb8so9ptXJlu' >>> bcrypt.gensalt() b'$2b$12$bzuGn3BE.kgH3nq5R6BJDO' >>> bcrypt.gensalt() b'$2b$12$1nwjw90b5ONVvUFWBuBTxu'
- salt값은 랜덤으로 생성된다.
- salt값은 랜덤으로 생성되는데, 어떻게 hash된 패스워드들을 비교할 수 있을까?
- hash된 패스워드에는 salt 값이 추가되어 있다.
>>> salt = bcrypt.gensalt() >>> salt b'$2b$12$KEi3lKW7D08IgUOUSpk/1O' >>> hashed_password = bcrypt.hashpw( password.encode('utf-8'), salt ... ) >>> hashed_password b'$2b$12$KEi3lKW7D08IgUOUSpk/1OyWyMgSzGTktOBaXTVFAq5o8Tvpc7zxm'
- salt값을 미리 저장해두고 hashed_password에 salt값을 지정해주면 hashed_password 안에 지정한 salt 값이 포함되어 있는 것을 볼 수 있다.
>>> type(hashed_password) <class 'bytes'>
- hash된 패스워드값은 bytes 타입이다. 따라서 데이터베이스에 hash된 패스워드 값을 decode하여 문자열로 만들고 이 값을 저장한다.
JWT
>>> encoded_jwt = jwt.encode( {'user-id': 5}, 'secret', algorithm='HS256' )
- payload에는 절대 패스워드나 이메일 등 개인 정보를 담아서는 안된다.
- 하지만 user_id는 노출되어도 단순한 숫자이기 때문에 데이터베이스에 접근 권한이 없는 한 의미없는 데이터이다.
>>> encoded_jwt 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyLWlkIjo1fQ.tBQu0HfnOYK7lL3tH5ImgsI-y4Jz1RKscJWbV3U2QMI'
>>> encoded_jwt 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyLWlkIjo1fQ.tBQu0HfnOYK7lL3tH5ImgsI-y4Jz1RKscJWbV3U2QMI' >>> type(encoded_jwt) <class 'str'>
- token의 타입을 확인하면 str 타입인것을 확인할 수 있다.
- 여기서 다른 분들은 bytes 타입이 나와서 왜 나는 str 타입으로 나오나 확인해 봤더니 PyJWT 버전 v2.0.0 이상이라면 jwt.encode의 반환값이 bytes 타입이 아니라 string 타입이라고 한다.
bcrypt
- bcrypt는 str 데이터가 아닌 Bytes 데이터를 암호화한다.
- 따라서 암호화시에 bytes화 해야 한다.
- 파이썬에서는 str을 encode하면 bytes(이진화)가 되고, bytes를 decode하면 str화 한다.
- encode, decode 시에 우리가 인식할 수 있는 형태로 변환하기 위해 'UTF-8' 유니코드 문자 규격을 사용한다.
회원가입 시 비밀번호 암호화해서 저장
hashed_password = bcrypt.hashpw(
data['password'].encode('utf-8'),bcrypt.gensalt()
).decode()
User.objects.create(
name = data['name'],
email = data['email'],
password = hashed_password,
mobile = data['mobile'],
address = data['address'],
birth_date = data['birth_date']
)
password를 DB에 처음 입력할 때 decode() 해야 하는 이유는?
bcrypt.hashpw('1234'.encode('utf-8'),bcrypt.gensalt()) 는 b'$2b$12$8EPF0NBZotiz0aPDVKwQHumq6dMhMnfP1BwC3xh2lKigedKeH4ucq'
인데,
bcrypt.hashpw('1234'.encode('utf-8'),bcrypt.gensalt()).decode()는
'$2b$12$8EPF0NBZotiz0aPDVKwQHumq6dMhMnfP1BwC3xh2lKigedKeH4ucq'
로 앞의 바이트라는 표시를 빼주기 때문이다
- 입력한 password는 str 타입이기 때문에 encode를 이용해 bytes 타입으로 변환한다.
- 이 때 bytes 타입으로 변환한 password 앞에는 b가 붙기 때문에 다시 decode를 해주어 맨 앞의 b를 삭제해준다.
로그인 시 비밀번호의 일치여부 확인
>>> bcrypt.checkpw(new_password.encode('utf-8'), hashed_password) True
- bcrypt.checkpw() 메소드가 비밀번호를 확인하는 작업을 한다.
- 메소드 규칙
- 입력받은 패스워드, 저장된 암호화된 패스워드
- 입력받은 패스워드와 저장된 암호화된 패스워드를 비교한다.
- 둘다 데이터 타입은 bytes 여야 한다.
user_info = User.objects.get(email=data['email'])
input_pw = data['password']
user_pw = user_info.password
if not bcrypt.checkpw(input_pw.encode('utf-8'), user_pw.encode('utf-8')):
return JsonResponse({'message':'INVALID_USER'}, status=401)
return JsonResponse({'message':'SUCCESS'}, status=200)
- input_pw와 user_pw 둘 다 현재 str 타입이기때문에 encode를 통해 bytes 타입으로 변환해 비교해준다.
로그인 완료 후 엑세스 토큰
데코레이터
- 데코레이터란 특정 함수의 바로 위에 붙어서 특정 함수 실행 전 먼저 실행 되어 사전에 하고 싶은 작업을 처리하는 함수이다.
- 생성할 데코레이터
- 사용자가 특정 기능을 실행하기 전에 사용자가 그 기능을 수행할 권한이 있는지 살펴보는 데코레이터
- 토큰으로 권한이 확인되지 않으면 특정 기능을 가진 함수는 실행되지 않음
게시물 등록 기능 구현
Posting app
- djando app 에서는 주로 다루는 데이터의 종류가 달라지는 시점에서 앱을 분리한다.
- 게시물은 사용자 데이터와는 그 성질이 달라 데이터베이스에서 테이블을 따라 관리한다.
- 주로 다루는 테이블이 달라지면 앱을 분리하는 것이 좋다.
Posting Model 생성
- 게시물을 등록하기 위해서는 사용자, 생성 시간, 이미지 url이 필요하다.
- 해당 게시물의 유저는 Foreign Key를 이용하여 이미 서비스에 가입된 사람으로 연결한다.
<참고>
- https://velog.io/@haileeyu21/ERROR-bcrypt-checkpw-ValueError-Invalid-salt
- https://velog.io/@devmin/Django-decorator-login-token-basic
- http://www.opennaru.com/opennaru-blog/jwt-json-web-token/
Author And Source
이 문제에 관하여([210726 TIL] Django), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@rimi0108/210726-TIL-Django저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)