我的网站搭建(第15天) 注册认证

  • 发布时间:2016年3月23日 15:52
  • 作者:杨仕航
* 该文是基于Python2.7开发的,最新Python3.x和Django2.x视频教程可以前往 >> Django2.0视频教程

前面弄好了登入和登出,现在需要继续完善,添加注册认证功能。

这个注册认证分几个步骤。

首先用户用邮箱注册了,但是没有激活。

接着网站后台发送一个带有激活链接的邮件给用户,用户打开链接即可完成激活步骤。

若不激活,就不可以评论。这样可以很大程度减少垃圾评论。


之前已经写好了前端页面,还需要配置链接和处理响应。打开user_ex文件夹的urls.py文件,添加下面的配置:

url(r'^user_reg$','user_ex.views.user_reg',name='user_reg'),
url(r'^user_active/([a-zA-Z]+)$','user_ex.views.user_active',name='user_active'),

一个是注册的链接,一个是激活的链接。

接下来,打开user_ex文件夹的views.py文件处理请求,添加如下代码:

from helper.crypto import encrypt,decrypt             #加密解密
from django.core.mail import EmailMultiAlternatives   #发送邮件
import time, re

@csrf_exempt
def user_reg(request):
    response_data = {}
    reg_name = ''

    try:
        reg_name = request.POST.get('reg_name')
        reg_pwd = request.POST.get('reg_pwd')

        if len(reg_name)*len(reg_pwd)==0:
            raise Exception(u"邮箱或密码为空")

        #匹配邮箱格式
        pattern = re.compile(r'^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$')
        match = pattern.match(reg_name)
        if not match:
            raise Exception(u"邮箱格式不正确")

        #验证密码长度
        if len(reg_pwd)<6:
            raise Exception(u"密码不能少于6位")

        #判断用户是否存在
        user=User.objects.filter(username=reg_name)
        if len(user)>0:
            raise Exception(u"该邮箱已经被注册")

        #创建新用户
        user=User(username=reg_name,email=reg_name)
        user.set_password(reg_pwd)  #这样才会对密码加密加盐处理
        user.is_active=False
        user.save()

        response_data['success'] = True
        response_data['message'] = u'注册成功,并发送激活邮件到您的邮箱。'
    except Exception as e:
        response_data['success'] = False
        response_data['message'] = e.message
    finally:
        if response_data['success']:
            try:
                #发送激活邮件
                #不想用uuid模块生成唯一ID保存到数据库中,也不想用django-registration
                #安全级别要求不高,所以简单写个加密解密的方法来处理
                active_code=get_active_code(reg_name)
                send_active_email(reg_name,active_code)
            except Exception as e:
                response_data['message']=u'注册成功,激活邮件发送失败。请稍后重试 '+e.message

            #注册成功,登录用户
            user = authenticate(username=reg_name, password=reg_pwd)
            if user is not None:
                login(request, user)

        return HttpResponse(json.dumps(response_data), content_type="application/json")

def get_active_code(email):
    """get active code by email and current date"""
    key=9
    encry_str='%s|%s' % (email,time.strftime('%Y-%m-%d',time.localtime(time.time())))
    active_code=encrypt(key,encry_str)
    return active_code

def send_active_email(email,active_code):
    """send the active email"""
    url='http://yshblog.com%s' % (reverse('user_active',args=(active_code,)))

    subject=u'[yshblog.com]激活您的帐号'
    message=u'''
        <h2>杨仕航的博客(<a href="http://yshblog.com/" target=_blank>yshblog.com</a>)<h2><br />
        <p>欢迎注册,请点击下面链接进行激活操作(7天后过期):<a href="%s" target=_balnk>%s</a></p>
        ''' % (url,url)

    send_to=[email]
    fail_silently=True  #发送异常不报错

    msg=EmailMultiAlternatives(subject=subject,body=message,to=send_to)
    msg.attach_alternative(message, "text/html")
    msg.send(fail_silently)

这里注册主要是写表添加记录和发送激活链接到邮箱。既然需要发送激活链接,那么需要先生成一串字符。

其中message.html模版是显示消息的,可以参考我的网站搭建(第13天) 用户认证:前言博文的末尾。

这里有个通用的处理逻辑:用uuid模块生成一个全球唯一的标识字符串,保存到对应的表中,和注册的用户有关联并且记录注册的时间用于验证激活链接有效期。这样处理的话,就需要创建一个用户拓展表,有些麻烦,而且安全性要求也不高。

我参考网上其他资料写了一个加密解密的方法,用于根据用户名和时间加密和解密,具体代码如下:

#coding:utf-8
"""encrpypt or decrypt the string"""
#2016-02-18

def encrypt(key, s):   
    """encrypt string(key is a number)"""
    b = bytearray(str(s).encode('utf-8'))
    n = len(b) # 求出 b 的字节数   
    c = bytearray(n*2)   
    j = 0   
    for i in range(0, n):   
        b1 = b[i]
        b2 = b1 ^ key # b1 = b2^ key
        c1 = b2 % 16   
        c2 = b2 // 16 # b2 = c2*16 + c1   
        c1 = c1 + 65   
        c2 = c2 + 65 # c1,c2都是0~15之间的数,加上65就变成了A-P 的字符的编码   
        c[j] = c1 
        c[j+1] = c2
        j = j+2   
    return c.decode('utf-8').lower()

def decrypt(key, s):
    """decrypt string(key is a number)"""
    c = bytearray(str(s).upper().encode('utf-8'))   
    n = len(c) # 计算 b 的字节数   
    if n % 2 != 0 :   
        return ""   
    n = n // 2   
    b = bytearray(n)   
    j = 0   
    for i in range(0, n):   
        c1 = c[j]   
        c2 = c[j+1]   
        j = j+2   
        c1 = c1 - 65   
        c2 = c2 - 65   
        b2 = c2*16 + c1   
        b1 = b2^ key   
        b[i]= b1   
    try:   
        return b.decode('utf-8')
    except:   
        return ""

这个方法就是helper包里面的crypto.py文件。

大致的注册和生成激活链接并发送邮件的代码就是这样,具体发送邮件功能还需要配置一下Settings.py文件。我的服务器是WebFaction,可以参考一下http://yshblog.com/blog/31


然后,还需要处理激活这个请求。继续在views.py文件加如下方法:

def user_active(request,active_code):
    """user active from the code"""
    #加错误处理,避免出错。出错认为激活链接失效
    #解密激活链接
    key=9
    data={}
    try:
        decrypt_str=decrypt(key,active_code)
        decrypt_data=decrypt_str.split('|')
        email=decrypt_data[0]                                   #邮箱
        create_date=time.strptime(decrypt_data[1], "%Y-%m-%d")  #激活链接创建日期
        create_date=time.mktime(create_date)            #struct_time 转成浮点型的时间戳

        day=int((time.time()-create_date)/(24*60*60))     #得到日期差
        if day>7:
            raise Exception(u'激活链接过期')

        #激活
        user=User.objects.filter(username=email)
        if len(user)==0:
            raise Exception(u'激活链接无效')
        else:
            user=User.objects.get(username=email)

        if user.is_active:
            raise Exception(u'该帐号已激活过了')
        else:
            user.is_active=True
            user.save()

        data['goto_page']=True
        data['message']=u'激活成功,欢迎访问!博主会陆续发表高质量的原创博文。'
    except IndexError as e:
        data['goto_page']=False
        data['message']=u'激活链接无效'
    except Exception as e:
        data['goto_page']=False
        data['message']=e
    finally:
        #激活成功就跳转到首页(message页面有自动跳转功能)
        data['goto_url']='/'
        data['goto_time']=3000
        return render_to_response('message.html',data)

这个激活方法主要是解密前面加密的字符串,然后匹配一下并修改一下表中的记录。


后端的代码写好之后,再修改一下前端的ajax注册请求。同样修改前面登录那个模版。在function(){}里面加入如下代码:

//注册
$('#reg_form').submit(function(){
    //验证
    var tip=$('#reg_tip');
    tip.text('');

    var reg_name=$('#reg_name').val();
    var reg_pwd=$('#reg_pwd').val();

    if(reg_name==''){
        tip.text('邮箱不能为空');
        return false;
    };
    if(!reg_name.match(/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/)){
        tip.text('请输入正确的邮箱格式');
        return false;
    }

    if(reg_pwd==''){
        tip.text('密码不能为空');
        return false    ;
    };
    if(reg_pwd.length<6){
        tip.text('密码不能少于6位');
    }

    //注册
    tip.text('注册中,请稍后...');
    $('#reg_csrf').val($.cookie('csrftoken'));
    $.ajax({
        type: "POST",
        data: $('#reg_form').serialize(),
        url: "{% url 'user_reg' %}",
        cache: false,
        dataType: "json",
        success: function(json, textStatus){
            var is_success = json['success'];
            if(is_success){
                tip.text('注册成功,页面处理中...');
                $('#reg_control').text(json['message']);

                window.setTimeout(function(){ 
                    window.location.reload(); 
                },3000);
            }else{
                tip.text(json['message']);
            };
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            tip.text("注册出错,请重试 "+errorThrown);
        }
    });
    return false;
});


到这里就完成了注册认证这个功能。后面就开始修改评论功能,加上用户验证。

另外,特别说明一下。大家参考一下思路和一些实现方法即可。若直接复制粘贴到自己的项目中,很大几率会出错的,毕竟文档结构不同。

上一篇:我的网站搭建(第16天) 只允许注册用户评论

下一篇:我的网站搭建(第14天) 登入和登出

评论列表

智慧如你,不想发表一下意见吗?

新的评论

清空