Django注册邮件验证失效:用户未验证链接即被创建
Got it, let's fix this issue right away. The problem with the original implementation is that it creates an active user account immediately when someone signs up. We need to adjust the flow so the account stays inactive until the user clicks the verification link in their email. Here's how to do it step by step:
1. Create an Inactive User on Registration
Instead of creating an active user by default, set is_active=False when saving the user. This blocks them from logging in until they verify their email.
Update your registration view code:
from django.contrib.auth.models import User from django.contrib.sites.shortcuts import get_current_site from django.core.mail import EmailMessage from django.template.loader import render_to_string from django.utils.encoding import force_bytes, force_text from django.utils.http import urlsafe_base64_encode def register(request): if request.method == 'POST': # Get form data (adjust based on your form structure) username = request.POST['username'] email = request.POST['email'] password = request.POST['password'] # Create user but keep it inactive initially user = User.objects.create_user(username=username, email=email, password=password) user.is_active = False # This is the critical line! user.save() # Generate verification token and send email (continue to step 2) # ... rest of your email logic here # Handle GET requests or form validation errors # ...
2. Generate a Secure Verification Token
We'll use Django's built-in token generator to create a unique, time-limited token for each user. Create a custom token generator (save this in a tokens.py file in your app):
from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.utils import six class AccountActivationTokenGenerator(PasswordResetTokenGenerator): def _make_hash_value(self, user, timestamp): # Include user's active status to invalidate tokens if account is activated early return ( six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.is_active) ) account_activation_token = AccountActivationTokenGenerator()
3. Send the Verification Email
In your registration view, add code to build the verification URL and send it to the user's email:
from .tokens import account_activation_token # Inside the POST block of your register view: current_site = get_current_site(request) mail_subject = 'Activate your account' message = render_to_string('account_activation_email.html', { 'user': user, 'domain': current_site.domain, 'uid': urlsafe_base64_encode(force_bytes(user.pk)), 'token': account_activation_token.make_token(user), }) to_email = email # Use the email submitted in the form email = EmailMessage(mail_subject, message, to=[to_email]) email.send() # Redirect to a page telling the user to check their inbox return redirect('registration_success')
Create the email template (account_activation_email.html) with the verification link:
Hi {{ user.username }}, Please click the link below to activate your account: http://{{ domain }}{% url 'activate' uidb64=uid token=token %} If you didn't request this, feel free to ignore this email.
4. Create the Activation View
Add a view to handle the verification link click, validate the token, and activate the user:
from django.shortcuts import redirect, HttpResponse from django.utils.http import urlsafe_base64_decode from django.contrib.auth import login from .tokens import account_activation_token def activate(request, uidb64, token): try: # Decode the user ID from the URL parameters uid = force_text(urlsafe_base64_decode(uidb64)) user = User.objects.get(pk=uid) except (TypeError, ValueError, OverflowError, User.DoesNotExist): user = None # Validate token and activate the user if everything checks out if user is not None and account_activation_token.check_token(user, token): user.is_active = True user.save() login(request, user) # Auto-login after activation (optional) return redirect('home') # Redirect to your app's home page else: return HttpResponse('Activation link is invalid or has expired.')
5. Update Your URLs
Add a URL pattern for the activation view in your app's urls.py:
from django.urls import path from . import views urlpatterns = [ # ... your existing URLs path('activate/<uidb64>/<token>/', views.activate, name='activate'), ]
Key Notes
- Django's Default Login Check: Django's built-in authentication system automatically blocks inactive users from logging in, so you don't need extra code to enforce this.
- Email Testing: For development, use the console backend to view emails directly in your terminal:
# In settings.py EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' - Token Expiry: The default token expires after 3 days. If you need a different timeframe, override the
_num_daysmethod in your custom token generator.
内容的提问来源于stack exchange,提问作者omkar more

