2014-07-24 10 views
17

Sto cercando di aggiungere la verifica dell'account e-mail in Django. Ho provato a utilizzare l'app di registrazione django per farlo, ma non sembra che sia stata aggiornata per essere pienamente compatibile con i modelli utente personalizzati che causano troppi problemi. Esiste un'altra app affidabile e ben documentata che mi consenta di inviare un'email di verifica sulla registrazione dell'utente in django?Django Custom User Email Verifica account

+0

Come gestisci effettivamente la registrazione di un nuovo utente? È un modulo personalizzato?In tal caso, puoi annullare il metodo di salvataggio per inviare la tua email (creare l'utente come inattivo, generare un link che invierai nell'e-mail, quando vai a questo link, attivare l'utente) –

+0

Sì, sto usando una custom UserCreationForm per creare l'utente. Quindi, all'interno di quel metodo di salvataggio, dopo 'user = super (UserCreationForm, self) .save (commit = False)', dovrei impostare 'user.is_active = False' e ​​quindi inviare una mail con il codice di conferma, e senza alcun plug-in in questo può essere fatto semplicemente? – sdotslezek

+0

Ti posterò come gestisco questo personalmente (giusto il tempo di copiare tutto: p), ma sì, a mio parere non hai bisogno di un'app o di un plugin –

risposta

44

Come posso gestire la registrazione e-mail personali:

Prima di tutto, il mio profilo si estende Django utenti (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() 

Nel forms.py, la classe di registrazione:

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 <[email protected]>', [datas['email']], fail_silently=False) 

Ora, nel views.py, dobbiamo gestire tutto questo, andiamo:

La vista registro:

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()) 

I punti di vista di attivazione:

#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) 

Infine, 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'), 

Con tutto ciò che si dovrebbe avere qualcosa da cui partire, utilizzare l'appropriato templatetags nelle e-mail .txt e HTML dovrebbe funzionare.

NB: questo codice non è perfetto, c'è una duplicazione (ad esempio, la generazione della chiave casuale potrebbe essere definita in una funzione), ma fa il lavoro. Inoltre: la chiave di attivazione non viene generata utilizzando le funzioni crittografiche appropriate. Un'alternativa è quella di usare una funzione come la seguente per generare le chiavi:

from django.utils.crypto import get_random_string 

def generate_activation_key(username): 
    chars = '[email protected]#$%^&*(-_=+)' 
    secret_key = get_random_string(20, chars) 
    return hashlib.sha256((secret_key + username).encode('utf-8')).hexdigest() 

NB2: Django send_mail non fornisce alcuno strumento per autenticare le email. Se vuoi autenticare le tue email (DKIM, SPF), ti consiglio di esaminare questo: https://djangosnippets.org/snippets/1995/

NB3: C'è un problema di sicurezza con la vista new_activation_link: dovrebbe verificare se l'utente che richiede il re-invio è quello giusto e anche se non è già autenticato. Ti lascio correggere.

+0

davvero geniale !! –

+0

@Raphael Laurent, quindi, come si dovrebbe fare il login con il token? La risposta sopra sta facendo 'save()' non loggando – LKM

+2

** Attenzione: ** questa risposta non usa un modo sicuro per generare la chiave di attivazione, ma si basa solo sul modulo 'random'. Un modo semplice per ottenere un valore sicuro è usare 'get_random_string' da' django.utils.crypto' (questo è usato per fornire il 'SECRET_KEY' di default quando si usa' startproject') o, se si usa python3.6>, usare il nuovo modulo 'secrets'. Altrimenti usa 'os.urandom' per ottenere dati casuali reali invece di' random.random'. – Bakuriu

1

Potreste anche essere interessati al semplice ma potente .

Basta usare VerifiedEmailField nei moduli:

from django import forms 
from verified_email_field.forms import VerifiedEmailField 

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

o nel vostro modelli:

from django.db import models 
from verified_email_field.models import VerifiedEmailField 

class User(models.Model): 
    email = VerifiedEmailField('e-mail') 

rende due campi di input: e-mail e verification code. Il codice di verifica viene inviato all'indirizzo e-mail utilizzando AJAX o durante il campo clean se non esiste un codice valido per l'e-mail specificata, quindi funziona anche senza javascript.