[210726 TIL] Django

9799 단어 TILdjangoTIL

해시 (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를 이용하여 이미 서비스에 가입된 사람으로 연결한다.

<참고>

좋은 웹페이지 즐겨찾기