忘记密码后,重置密码的逻辑

逻辑:

主要是借鉴了Django内置的重置密码的逻辑。

1、如果忘记密码,用户填写注册时的邮箱;
2、根据邮箱,得到用户User object,并生成uidb64和token(用户加密);
3、将得到的重置密码链接发到邮箱;
4、用户根据得到的链接填写新密码;

1,2步骤

主要是根据注册邮箱得到重置密码链接。源代码:

class ForgetPasswdView(FormView):
    template_name = 'account/forgetpasswd.html'
    form_class = UserPasswordResetForm
    success_url = '/'

    def form_valid(self, form):
        try:
            form.send()
        except ValidationError,e:
            form.add_error('email', e)
            form._clean_form()
            return self.render_to_response(self.get_context_data(form=form))
        return HttpResponse('check email')




class UserPasswordResetForm(PasswordResetForm):

    def send(self):
        email = self.cleaned_data['email']
        DOMAIN = settings.DOMAIN
        
        DOMAIN = DOMAIN.rstrip('/')
        token_generator = default_token_generator
        if len(list(self.get_users(email))) == 0:
            raise forms.ValidationError('user not exists')
        for user in self.get_users(email):
            uid = urlsafe_base64_encode(force_bytes(user.id))
            token = token_generator.make_token(user)
            message = "\n".join([
                '您的用户名是:{0}'.format(user.username),
                '请访问该链接,进行密码重置:',
                '/'.join([DOMAIN, 'account/resetpwd', uid, token]) + '/',
            ])
            send_mail('注册用户密码重置', message, None, [email])

上面一个是类视图,一个是Form类。类视图只写基本逻辑,具体处理部分写在form类中。代码中form.get_users(email)会根据邮箱得到用户类,Django中的源代码如下:

class PasswordResetForm(forms.Form):
    email = forms.EmailField(label=_("Email"), max_length=254)


    def get_users(self, email):
        """Given an email, return matching user(s) who should receive a reset.

        This allows subclasses to more easily customize the default policies
        that prevent inactive users and users with unusable passwords from
        resetting their password.
        """
        active_users = get_user_model()._default_manager.filter(
            email__iexact=email, is_active=True)
        return (u for u in active_users if u.has_usable_password())

得到用户后,通过用户类,生成uid和token。并将相应链接发到注册的邮箱。

3、收到的邮件:

邮件内容:
您的用户名是:hbnnlong
请访问该链接,进行密码重置:
http://hbnnforever.cn/account/resetpwd/Mg/4ck-854a0f660ddf/

Mg就是用户id经过编码后的字符串,后面的即为token。我设定了token的有效期是1天。

4、用户访问链接进行重置密码

def user_decorator(method):
    def wrapper(instance,request, *args, **kwargs):
        token_generator = default_token_generator
        UserModel = get_user_model()
        uidb64, token = instance.kwargs['uidb64'], instance.kwargs['token']
        assert uidb64 is not None and token is not None
        try:
            uid = urlsafe_base64_decode(uidb64)
            user = UserModel._default_manager.get(pk=uid)
        except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
            user = None
        if user is not None and token_generator.check_token(user, token):
            instance.request.user = user
            return method(instance,request, *args, **kwargs)
        else:
            return HttpResponse('link expires')
    return wrapper


class ResetPasswdView(FormView):
    template_name = 'account/resetpwd.html'
    form_class = SetPasswordForm
    success_url = '/'

    @user_decorator
    def get(self, request, *args, **kwargs):
        return super(ResetPasswdView,self).get(request, *args, **kwargs)


    def get_form(self, form_class=None):
        if form_class is None:
            form_class = self.get_form_class()
        return form_class(self.request.user, **self.get_form_kwargs())

    def form_valid(self, form):
        form.save()
        return HttpResponseRedirect('/account/login/')

    @user_decorator
    def post(self, request, *args, **kwargs):
        return super(ResetPasswdView,self).post(request,*args,**kwargs)

    在上面,我写了一个装饰器,因为SetPasswordForm需要传入当前的User,那对于GET和POST请求都需要,因此为了避免代码重复,我就写了一个装饰器user_decorator,用来获取user。

    这个装饰器与普通的函数装饰器不同,它是一个类方法的装饰器,参数一定要带上代表实例本身的参数,比如我用了instance。其实,Django自己还有一个将函数装饰器转为类方法的装饰器:method_decorator。

   上面就是整个重置密码的逻辑。

 

--------EOF---------
微信分享/微信扫码阅读