Django Custom User Email Account Verification

前端 未结 2 1587
挽巷
挽巷 2020-12-12 11:43

I am looking to add email account verification in Django. I have attempted using the django-registration app to do so, but it doesn\'t appear that it has been updated to be

相关标签:
2条回答
  • 2020-12-12 11:53

    You may also be interested in the simple but powerful django-verified-email-field.

    Simply use VerifiedEmailField in Your forms:

    from django import forms
    from verified_email_field.forms import VerifiedEmailField
    
    class RegistrationForm(forms.ModelForm):
        email = VerifiedEmailField(label='email', required=True)
    

    Or in Your models:

    from django.db import models
    from verified_email_field.models import VerifiedEmailField
    
    class User(models.Model):
        email = VerifiedEmailField('e-mail')
    

    It renders two input fields: e-mail and verification code. The verification code is sent to the e-mail address using AJAX or during field's clean if there is no valid code for given e-mail, so it works even without javascript.

    0 讨论(0)
  • 2020-12-12 12:03

    How I handle the email registration personally:

    First of all, my Profile extending Django Users (models.py):

    class Profile(models.Model):
        user = models.OneToOneField(User, related_name='profile') #1 to 1 link with Django User
        activation_key = models.CharField(max_length=40)
        key_expires = models.DateTimeField()
    

    In forms.py, the Registration class :

    class RegistrationForm(forms.Form):
        username = forms.CharField(label="",widget=forms.TextInput(attrs={'placeholder': 'Nom d\'utilisateur','class':'form-control input-perso'}),max_length=30,min_length=3,validators=[isValidUsername, validators.validate_slug])
        email = forms.EmailField(label="",widget=forms.EmailInput(attrs={'placeholder': 'Email','class':'form-control input-perso'}),max_length=100,error_messages={'invalid': ("Email invalide.")},validators=[isValidEmail])
        password1 = forms.CharField(label="",max_length=50,min_length=6,
                                    widget=forms.PasswordInput(attrs={'placeholder': 'Mot de passe','class':'form-control input-perso'}))
        password2 = forms.CharField(label="",max_length=50,min_length=6,
                                    widget=forms.PasswordInput(attrs={'placeholder': 'Confirmer mot de passe','class':'form-control input-perso'}))
    
        #recaptcha = ReCaptchaField()
    
        #Override clean method to check password match
        def clean(self):
            password1 = self.cleaned_data.get('password1')
            password2 = self.cleaned_data.get('password2')
    
            if password1 and password1 != password2:
                self._errors['password2'] = ErrorList([u"Le mot de passe ne correspond pas."])
    
            return self.cleaned_data
    
        #Override of save method for saving both User and Profile objects
        def save(self, datas):
            u = User.objects.create_user(datas['username'],
                                         datas['email'],
                                         datas['password1'])
            u.is_active = False
            u.save()
            profile=Profile()
            profile.user=u
            profile.activation_key=datas['activation_key']
            profile.key_expires=datetime.datetime.strftime(datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
            profile.save()
            return u
    
        #Sending activation email ------>>>!! Warning : Domain name is hardcoded below !!<<<------
        #The email is written in a text file (it contains templatetags which are populated by the method below)
        def sendEmail(self, datas):
            link="http://yourdomain.com/activate/"+datas['activation_key']
            c=Context({'activation_link':link,'username':datas['username']})
            f = open(MEDIA_ROOT+datas['email_path'], 'r')
            t = Template(f.read())
            f.close()
            message=t.render(c)
            #print unicode(message).encode('utf8')
            send_mail(datas['email_subject'], message, 'yourdomain <no-reply@yourdomain.com>', [datas['email']], fail_silently=False)
    

    Now, in views.py, we need to handle all that, let's go :

    The register view:

    def register(request):
        if request.user.is_authenticated():
            return redirect(home)
        registration_form = RegistrationForm()
        if request.method == 'POST':
            form = RegistrationForm(request.POST)
            if form.is_valid():
                datas={}
                datas['username']=form.cleaned_data['username']
                datas['email']=form.cleaned_data['email']
                datas['password1']=form.cleaned_data['password1']
    
                #We generate a random activation key
                salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
                usernamesalt = datas['username']
                if isinstance(usernamesalt, unicode):
                    usernamesalt = usernamesalt.encode('utf8')
                datas['activation_key']= hashlib.sha1(salt+usernamesalt).hexdigest()
    
                datas['email_path']="/ActivationEmail.txt"
                datas['email_subject']="Activation de votre compte yourdomain"
    
                form.sendEmail(datas)
                form.save(datas) #Save the user and his profile
    
                request.session['registered']=True #For display purposes
                return redirect(home)
            else:
                registration_form = form #Display form with error messages (incorrect fields, etc)
        return render(request, 'siteApp/register.html', locals())
    

    The activation views :

    #View called from activation email. Activate user if link didn't expire (48h default), or offer to
    #send a second link if the first expired.
    def activation(request, key):
        activation_expired = False
        already_active = False
        profile = get_object_or_404(Profile, activation_key=key)
        if profile.user.is_active == False:
            if timezone.now() > profile.key_expires:
                activation_expired = True #Display: offer the user to send a new activation link
                id_user = profile.user.id
            else: #Activation successful
                profile.user.is_active = True
                profile.user.save()
    
        #If user is already active, simply display error message
        else:
            already_active = True #Display : error message
        return render(request, 'siteApp/activation.html', locals())
    
    def new_activation_link(request, user_id):
        form = RegistrationForm()
        datas={}
        user = User.objects.get(id=user_id)
        if user is not None and not user.is_active:
            datas['username']=user.username
            datas['email']=user.email
            datas['email_path']="/ResendEmail.txt"
            datas['email_subject']="Nouveau lien d'activation yourdomain"
    
            salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
            usernamesalt = datas['username']
            if isinstance(usernamesalt, unicode):
                usernamesalt = usernamesalt.encode('utf8')
            datas['activation_key']= hashlib.sha1(salt+usernamesalt).hexdigest()
    
            profile = Profile.objects.get(user=user)
            profile.activation_key = datas['activation_key']
            profile.key_expires = datetime.datetime.strftime(datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
            profile.save()
    
            form.sendEmail(datas)
            request.session['new_link']=True #Display: new link sent
    
        return redirect(home)
    

    Finally, in urls.py:

    url(r'^register/$', 'register'),
    url(r'^activate/(?P<key>.+)$', 'activation'),
    url(r'^new-activation-link/(?P<user_id>\d+)/$', 'new_activation_link'),
    

    With all that you should have something to start with, use the appropriate templatetags in the .txt emails and HTML and it should work.

    NB: This code isn't perfect, there is duplication (for instance, the generation of the random key could be defined in a function), but it does the job. Also: the activation key is not generated using proper cryptographic functions. An alternative is to use a function like the following to generate the keys:

    from django.utils.crypto import get_random_string
    
    def generate_activation_key(username):
        chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
        secret_key = get_random_string(20, chars)
        return hashlib.sha256((secret_key + username).encode('utf-8')).hexdigest()
    

    NB2: Django send_mail doesn't provide any tools to authenticate your emails. If you want to authenticate your emails (DKIM, SPF), I advise you to look into this: https://djangosnippets.org/snippets/1995/

    NB3: There is a security issue with the view new_activation_link: it should check if the user requesting the re-send is the right one and also if he isn't already authenticated. I let you correct that.

    0 讨论(0)
提交回复
热议问题