models.py 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import unicodedata
  2. from datetime import datetime, time
  3. from django.core.mail import send_mail
  4. from django.db import models
  5. from django.utils.translation import gettext_lazy as _
  6. from django.utils.http import base36_to_int, int_to_base36
  7. from django.conf import settings
  8. from django.utils.crypto import constant_time_compare, salted_hmac
  9. from .validators import ASCIIUsernameValidator
  10. class User(models.Model):
  11. username = models.CharField(
  12. _('username'),
  13. max_length=25,
  14. unique=True,
  15. help_text=_('Required. 25 characters or fewer. Letters, digits and _ only.'),
  16. validators=[ASCIIUsernameValidator()],
  17. error_messages={
  18. 'unique': _("A _user with that username already exists."),
  19. },
  20. )
  21. password = models.CharField(_('password'), max_length=128)
  22. last_login = models.DateTimeField(_('last login'), blank=True, null=True)
  23. email = models.EmailField(_('email address'), unique=True)
  24. class Meta:
  25. db_table = '_user'
  26. verbose_name = verbose_name_plural = '用户信息表'
  27. def set_password(self, password):
  28. # TODO: 密码强度检验,密码hash存储
  29. self.password = password
  30. def send_email(self, subject, message, from_email=None, **kwargs):
  31. send_mail(subject, message, from_email, [self.email], **kwargs)
  32. def make_token(self):
  33. return self._make_token(_timestamp())
  34. def check_token(self, token):
  35. if not token:
  36. return False
  37. try:
  38. ts_b36, hash_str = token.split('-')
  39. except ValueError:
  40. return False
  41. try:
  42. ts = base36_to_int(ts_b36)
  43. except ValueError:
  44. return False
  45. if self._make_token(ts) != token:
  46. return False
  47. timestamp = _timestamp()
  48. if (timestamp - ts) > settings.PASSWORD_RESET_TIMEOUT:
  49. return False
  50. return True
  51. def _make_token(self, timestamp):
  52. ts_b36 = int_to_base36(timestamp)
  53. salt = settings.SALT
  54. value = self._make_hash_value(timestamp)
  55. secret = settings.SECRET_KEY
  56. hash_str = salted_hmac(
  57. salt, value, secret=secret, algorithm='sha256'
  58. ).hexdigest()[::2]
  59. token = "%s-%s" % (ts_b36, hash_str)
  60. return token
  61. def _make_hash_value(self, timestamp):
  62. return f'{self.pk}{self.password}{timestamp}{self.email}'
  63. def _timestamp():
  64. dt = datetime.now()
  65. return int((dt - datetime(2001, 1, 1)).total_seconds())
  66. class LoginToken(models.Model):
  67. user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tokens')
  68. token = models.CharField(max_length=256)