Django account_activation_token.check_token 在 constant_time_compare() 中总是返回 False

Zul*_*tra 3 python django token

我有一个ActivationTokenGenerator创建将用于帐户验证的令牌,该令牌将通过电子邮件发送。例如,我使用时间戳、ID 和用户活动状态等参数对其进行了配置:

from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.utils import six


    class ActivationTokenGenerator(PasswordResetTokenGenerator):

        def _make_hash_value(self, user, timestamp):
            return six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.is_active)


    account_activation_token = ActivationTokenGenerator()
Run Code Online (Sandbox Code Playgroud)

然后我使用account_activation_token用于在我发送的验证电子邮件中生成令牌send_mail

@classmethod
    def send_email(cls, request, user):
        current_site = get_current_site(request)
        mail_subject = 'Activate your Poros account.'
        message = render_to_string('email_verification.html', {
            'user': user,
            'domain': current_site.domain,
            'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
            'token': account_activation_token.make_token(user),
        })
        to_email = user.email
        email = EmailMessage(
            mail_subject, message, to=[to_email]
        )
        email.send()
Run Code Online (Sandbox Code Playgroud)

一切看起来都很完美的电子邮件发送的令牌包含在一个 url 中,其模式如下:

url(r'activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        activate, name='activate'),
Run Code Online (Sandbox Code Playgroud)

在电子邮件中看起来像这样:

http://{{ domain }}{% url 'activate' uidb64=uid token=token %}
Run Code Online (Sandbox Code Playgroud)

然后当链接被点击时,它会调用这个activate视图:

from django.http import HttpResponse

from django.contrib.auth import login

from django.utils.encoding import force_text
from django.utils.http import urlsafe_base64_decode
from accounts.utilities import account_activation_token
from accounts.models import User


def activate(request, uidb64, token):
    try:
        id = force_text(urlsafe_base64_decode(uidb64))
        print(id)
        user = User.objects.get(pk=id)
        print(user)
    except(TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None

    print(token)

    if user is not None and account_activation_token.check_token(user, token):
        user.is_active = True
        user.save()
        login(request, user)
        return HttpResponse('Thank you for your email confirmation. Now you can login your account.')
    else:
        return HttpResponse('Activation link is invalid!')
Run Code Online (Sandbox Code Playgroud)

但是activate视图总是返回激活链接无效。我试图将其追踪到account_activation_token.check_token(user, token)

我试图更深入地调试在 Django 中,PasswordResetTokenGenerator我发现check_token()有一个步骤来检查时间戳/ uid,如下所示:

# Check that the timestamp/uid has not been tampered with
    if not constant_time_compare(self._make_token_with_timestamp(user, ts), token):
        return False
Run Code Online (Sandbox Code Playgroud)

其中调用constant_time_compare

def constant_time_compare(val1, val2):
    """Return True if the two strings are equal, False otherwise."""
    return hmac.compare_digest(force_bytes(val1), force_bytes(val2))
Run Code Online (Sandbox Code Playgroud)

我真的不明白 this 的较低级别发生了什么token_check。解决这个问题的更好方法是什么?

小智 7

更改此行:

return six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.is_active)
Run Code Online (Sandbox Code Playgroud)

到这一行:

return six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.username)
Run Code Online (Sandbox Code Playgroud)

它有效。我用这种方法解决。但不知道原因。