在我们的Django网站上,我们需要创建一个管理操作,该操作将向所选用户发送邮件,要求他们更新其地址。为此,我看了一下django的内置密码重置工具,该工具使用令牌。
我复制并更改了一些令牌:
class AddressEmailTokenGenerator(object):
"""
Strategy object used to generate and check tokens for the password
reset mechanism.
"""
def make_token(self, address):
"""
Returns a token that can be used once to do a password reset
for the given user.
"""
return self._make_token_with_timestamp(address, self._num_days(self._today()))
def check_token(self, address, token):
"""
Check that a password reset token is correct for a given user.
"""
# Parse the token
try:
ts_b36, hash = token.split("-")
except ValueError:
return False
try:
ts = base36_to_int(ts_b36)
except ValueError:
return False
# Check that the timestamp/uid has not been tampered with
if not constant_time_compare(self._make_token_with_timestamp(address, ts), token):
return False
# Check the timestamp is within limit
if (self._num_days(self._today()) - ts) > settings.PASSWORD_RESET_TIMEOUT_DAYS:
return False
return True
def _make_token_with_timestamp(self, address, timestamp):
# timestamp is number of days since 2001-1-1. Converted to
# base 36, this gives us a 3 digit string until about 2121
ts_b36 = int_to_base36(timestamp)
# By hashing on the internal state of the user and using state
# that is sure to change (the password salt will change as soon as
# the password is set, at least for current Django auth, and
# last_login will also change), we produce a hash that will be
# invalid as soon as it is used.
# We limit the hash to 20 chars to keep URL short
key_salt = "django.contrib.auth.tokens.PasswordResetTokenGenerator"
# Ensure results are consistent across DB backends
# login_timestamp = user.last_login.replace(microsecond=0, tzinfo=None)
value = (six.text_type(address.pk) + six.text_type(timestamp))
hash = salted_hmac(key_salt, value).hexdigest()[::2]
return "%s-%s" % (ts_b36, hash)
def _num_days(self, dt):
return (dt - date(2001, 1, 1)).days
def _today(self):
# Used for mocking in tests
return date.today()
我有我的管理员行动:
def send_update_request(modeladmin, request, queryset):
test_mail = "test@test.ch"
for address in queryset:
token_generator = AddressEmailTokenGenerator()
token = token_generator.make_token(address)
print token
form_url = reverse("update_address", kwargs={"token":token})
send_mail("Address", form_url, "test@test.ch", [test_mail], fail_silently=False)
现在问题是:如何从网址中的令牌获取相应地址的pk?
我的观点:
class UpdateAddressView(CreateView):
model = UpdateAddress
def get_context_data(self, **kwargs):
context = super(UpdateAddressView, self).get_context_data(**kwargs)
token = self.kwargs['token']
print token
#The token here is something like "3zi-ebf1da92b5e8bd3aaf86"
return context
答案 0 :(得分:1)
你不是,这不是它的运作方式。 Django重置密码视图有两个组件:用户ID的base-64编码字符串和用于检查有效性的标记。解码ID非常简单,只需拨打base64.urlsafe_b64decode
即可,但根本无法对令牌进行解码:它会进行哈希处理和盐渍处理,您唯一能做的就是使用用户对象创建一个新令牌并检查它们是否匹配。