Djoser账户验证系统底层工作原理解析及自建验证系统时的UID与Token相关技术问题咨询
Great questions! Let's break this down step by step since I've worked with Djoser and built custom verification flows before.
1. How Djoser's Account Verification Works Under the Hood
Djoser is built on top of Django REST Framework (DRF) and leans heavily into Django's built-in auth system, so its verification flow is a structured extension of that:
- Registration Submission: When a user sends their signup data (email, password, etc.), Djoser first validates the input against your configured rules (like password complexity, unique email). If valid, it creates a
Userinstance withis_active=False(so the user can't log in yet). - Token & UID Generation: Next, it generates two key values:
UID: A base64-encoded version of the user's primary key (or a unique identifier for the user) – this lets the backend quickly look up the right user without exposing raw IDs directly.- Verification Token: A cryptographically signed token, not a JWT. It's generated using a token generator that combines user-specific data (like user ID, password hash, last login timestamp) with a timestamp to enforce expiration. Djoser uses a variant of Django's
PasswordResetTokenGeneratorfor this.
- Email Delivery: Djoser constructs a verification URL with the UID and Token, then sends it to the user's email via Django's email backend.
- Verification Confirmation: When the user clicks the link, your frontend (or Djoser's default view) sends a POST request with UID and Token to the backend. Djoser decodes the UID to find the user, re-computes the expected token using the same generator, and checks if the submitted token matches and hasn't expired. If everything checks out, it sets
user.is_active=Trueand completes the verification.
2. Verification Tokens: What They Are, How They Work, & Building a Custom Flow
What's the Verification Token? Is it JWT?
No, it's not a JWT. JWTs are designed for stateless authentication (carrying user claims) and can be reused until they expire. Djoser's verification token is a one-time, short-lived cryptographically signed token built specifically for account verification (or password resets). It's tied directly to the user's current state (like their password hash) – if the user changes their password before verifying, the old token becomes invalid automatically.
How the Token Works
The token is generated using HMAC-SHA256 encryption. The generator uses a combination of:
- The user's unique ID
- The user's password hash (so if the password changes, the token is no longer valid)
- A timestamp (to set an expiration window, usually 1-2 days)
- Optional user state (like
is_activestatus)
When verifying, the backend re-calculates the token using the same inputs and compares it to the submitted token. If they match and the timestamp is within the expiration window, the token is valid.
Building a Custom Verification Flow (Without Djoser)
Let's walk through a Django/DRF-based implementation – the core logic translates to other frameworks too:
Step 1: Create a Custom Token Generator
First, make a token generator class (similar to Django's built-in one):
from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.utils import six class AccountVerificationTokenGenerator(PasswordResetTokenGenerator): def _make_hash_value(self, user, timestamp): # Combine user data that changes when the user's state updates return ( six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.is_active) + six.text_type(user.password) ) account_verification_token = AccountVerificationTokenGenerator()
Step 2: Build the Registration View
Handle user signup, generate the token/UID, and send the verification email:
from django.core.mail import send_mail from django.utils.http import urlsafe_base64_encode from django.utils.encoding import force_bytes from rest_framework import generics, status from rest_framework.response import Response from .models import User from .serializers import RegisterSerializer from .tokens import account_verification_token class RegisterView(generics.CreateAPIView): serializer_class = RegisterSerializer def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user = serializer.save() user.is_active = False # Deactivate until verification user.save() # Generate UID and Token uid = urlsafe_base64_encode(force_bytes(user.pk)) token = account_verification_token.make_token(user) # Construct verification URL (replace with your frontend URL) verification_url = f"https://your-frontend.com/verify?uid={uid}&token={token}" # Send email send_mail( "Verify Your Account", f"Click the link to verify your account: {verification_url}", "no-reply@yourdomain.com", [user.email], fail_silently=False, ) return Response( {"message": "Registration successful. Check your email for verification link."}, status=status.HTTP_201_CREATED )
Step 3: Build the Verification View
Validate the UID and Token, then activate the user:
from django.utils.http import urlsafe_base64_decode from django.utils.encoding import force_str from rest_framework import generics, status from rest_framework.response import Response from .models import User from .tokens import account_verification_token class VerifyAccountView(generics.GenericAPIView): def post(self, request, *args, **kwargs): uid = request.data.get("uid") token = request.data.get("token") try: # Decode UID to get user ID user_id = force_str(urlsafe_base64_decode(uid)) user = User.objects.get(pk=user_id) except (TypeError, ValueError, OverflowError, User.DoesNotExist): return Response({"error": "Invalid UID"}, status=status.HTTP_400_BAD_REQUEST) # Verify token if account_verification_token.check_token(user, token): user.is_active = True user.save() return Response({"message": "Account verified successfully"}, status=status.HTTP_200_OK) else: return Response({"error": "Invalid or expired token"}, status=status.HTTP_400_BAD_REQUEST)
Step 4: Frontend Handling
- When the user clicks the verification link, parse the
uidandtokenfrom the URL parameters. - Render a simple page with a "Verify Account" button (or auto-submit a POST request to your verification endpoint with the UID and Token).
- Display success/error messages based on the backend response.
内容的提问来源于stack exchange,提问作者Melly

