Comment.objects.create(feed=Feed.objects.get(
id=data['feed_id']), account=user, content=data['text'])
// Or
Comment.objects.create(feed=Feed.objects.get(
id=data['feed_id']), account_id=2, content=data['text'])
class RegisterView(View):
def post(self, request):
data = json.loads(request.body)
try:
e_password = bcrypt.hashpw(
data['password'].encode('utf-8'), bcrypt.gensalt())
Account.objects.create(
email_or_phone=data['email_or_phone'],
realname=data['realname'],
username=data['username'],
password=e_password.decode('utf-8'),
)
// if registered successfully
return JsonResponse({'message': 'Registration Successful!'}, status=200)
except IntegrityError: // Fields that are unique=True
return JsonResponse({'message': 'EXISTING_VALUE'}, status=400)
except KeyError:
return JsonResponse({'message': 'INVALID_KEY'}, status=400)
Unicode-objects must be encoded before hashing
(1) bytes(‘1234’,’utf-8’) >>> b’1234’
(2)
b = ‘1234’
c = b.encode(‘utf-8’) # unicode text representation method
// c.decode('utf-8') will result in a string now
Next,
a = bcrypt.hashpw(c, bcrypt.gensalt())
Or,
a = bcrypt.hashpw(‘1234’.encode(‘utf-8’), bcrypt.gensalt())
>>> b'2b$12FrZe6p0eeCcusxKPQbIrguJxyzZmm5CSBRvLZthyK5uhI.7u5qOae'
Once you hash password with bcrypt, you cannot decode it back to the original String in a comprehensive manner. When you do try to decode it, Python will simply get rid of the 'b' (binary) notation in front of the hashed result and return it:
>>> 2b$12FrZe6p0eeCcusxKPQbIrguJxyzZmm5CSBRvLZthyK5uhI.7u5qOae'
Hence, when a user attempts to login with a password input, you need to encode the input password and compare it with already encoded password stored in your database, rather than the way around.
bcrypt.checkpw(‘1234’.encode(‘utf-8’), a.encode('utf-8'))
The password input to be evaluated does not require gensalt() or hashpw() operations. bcrypt.haspw() returns an encrypted result that contains information about which hashing algorithm is used for encryption, number of key stretches, and salts. bcypt.checkpw() can get hints from this information and accurately evaluate whether the input password is the same as the one in the databse. Moreover, gensalt() utilizes different salts each time, which means that providing gensalt() option to the input password can result in comparison errors.
Because the data type for password field in the database is CharField, the password had been decoded before being saved. Therefore, the password in the database also needs to be encoded before being compared with the input password.
class LoginView(View):
# post method: user log-in with any one of username, email, or phone
def post(self, request):
data = json.loads(request.body)
try:
if Account.objects.filter(
Q(username=data.get('username')) |
Q(email_or_phone=data.get('email_or_phone'))
).exists():
user = Account.objects.get(
Q(username=data.get('username')) |
Q(email_or_phone=data.get('email_or_phone'))
)
if bcrypt.checkpw(data['password'].encode('utf-8'), user.password.encode('utf-8')):
# if password is correct
token = jwt.encode({'user_id': user.id},
SECRET_KEY, algorithm=HASH)
return JsonResponse({'message': 'login successful!', 'token': token.decode('utf-8')}, status=200)
return JsonResponse({'message': 'Incorrect id or password'}, status=400)
except KeyError:
return JsonResponse({'message': 'INVALID_KEY'}, status=400)
a = jwt.encode({‘user_id’:1}, ‘secret’, algorithm=‘HS256’)
b = jwt.decode(a, ‘secret’, algorithm=‘HS256’)
Unlike bcrypt, JWT can be decrypted. Moreover, only the signature part is encrypted, and header and payload are simply encoded in base64. The signature is encrypted with a hash algorithm of your choice, a SECRET KEY, the contents of the header and the payload.
Having a login decorator simplicates authorization process. It checks if the user's request contains a valid token, and whether the user is valid and has logged in.
def login_decorator(func):
def wrapper(self, request, *args, **kwargs):
try:
if request.headers.get('Authorization'):
user_token = request.headers.get('Authorization')
user_info = jwt.decode(user_token, SECRET_KEY, HASH)
if Account.objects.filter(pk=user_info['user_id']).exists():
user = Account.objects.get(pk=user_info['user_id'])
request.user = user # request 에 존재하지 않던 user 정보를 추가
return func(self, request, *args, **kwargs)
else:
// Toekn's user information does not match database information: Account does not exist
return JsonResponse({'message': 'No matching account'}, status=401)
// header is missing token: the user needs to log in first
return JsonResponse({'message': 'No token'}, status=401)
// Token's user information is invalid
except KeyError:
return JsonResponse({'message': 'INVALID_KEY'}, status=400)
except jwt.DecodeError:
return JsonResponse({'message': 'INVALID_TOKEN'}, status=401)
return wrapper