个人网站搭建(Day 19)— Django-User 用户邮箱认证

这篇博客主要是来讲一下我升级过后的用户users模块,从原来的Schnee的users模块继承而来,但是加入了用户邮箱验证等功能。

参考博客:杨仕航—我的网站搭建(第15天) 注册认证

在新的用户系统中,我默认只能够以合法的邮箱作为用户名来注册,这样能够避免出现奇奇怪怪的用户名,也便于后续的通过邮件激活用户的操作。

用户在网站注册新用户后,会创建一个未激活的新用户(无法登录网站),需要点击通过激活邮件中的链接进行激活后方可登录。

首先是urls设计,包括注册链接以及激活链接:

 path('register/', views.user_reg, name = 'register'),

 path('active/', views.user_active, name = 'active'),

修改项目settings.py,添加邮箱部分设置:

EMAIL_HOST = "smtp.sina.com"
EMAIL_PORT = 25
EMAIL_HOST_USER = "[email protected]"            # 你的邮箱账号
EMAIL_HOST_PASSWORD = "xxxxxxxxx"                 # 你的邮箱密码
EMAIL_USE_TLS = False                             # 这里是 False
EMAIL_FROM = "[email protected]"                 # 你的邮箱账号
DEFAULT_FROM_EMAIL = SERVER_EMAIL = EMAIL_HOST_USER

注意:在这里我用的是新浪的邮箱,经过测试QQ邮箱也可以使用,最初使用163邮箱的时候会发现我的邮件被认为是垃圾邮件(状态码:554 DT:SPM)而不能成功发送出去,多次调试后无果采用其他邮箱。

接下来是视图函数设计,这部分很大程度上参考了杨仕航先生的博客:

from django.core.mail import send_mail
from helper.crypto import encrypt, decrypt

def get_active_code(email):
    """ 利用时间及邮箱获取激活码 """
    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):
    """ 发送激活邮件 """
    url = settings.CUR_HOST + 'users/active/' + active_code
    subject = '[Schnee]激活你的账号'
    message = '''
        

Schnee.com


点击下面的链接进行激活操作(7天后过期) Schnee激活链接

''' % url send_mail(subject, '', '[email protected]', [email], fail_silently=False, html_message=message) def user_reg(request): response_data = {} user_form = UserCreationForm() if request.method == 'POST': try: reg_name = request.POST['username'] reg_pwd1 = request.POST['password1'] reg_pwd2 = request.POST['password2'] if len(reg_name) * len(reg_pwd1) * len(reg_pwd2) == 0: raise Exception('邮箱或密码为空') #匹配邮箱格式 pattern = re.compile(r'^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$') match = pattern.match(reg_name) if not match: raise Exception('请使用邮箱作为用户名,邮箱格式错误') pattern = re.compile(r'^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,16}$') match = pattern.match(reg_pwd1) if not match: raise Exception("密码必须包含大小写字母和数字的组合,可以使用除'.'之外的特殊字符,长度在8-16之间") if '.' in reg_pwd1: raise Exception('密码不合法,换一个试试?') # 判断用户名是否已经被注册 user = User.objects.filter(username=reg_name) if len(user) > 0: raise Exception('该邮箱已被注册') if reg_pwd1 != reg_pwd2: raise Exception('两次输入的密码不一致') user_form = UserCreationForm(data=request.POST) # if user_form.is_valid(): new_user = user_form.save() new_user.email = reg_name new_user.is_active = False new_user.save() response_data['success'] = True response_data['message'] = '注册成功,已发送激活邮件到你的邮箱,请前往激活' except Exception as e: response_data['success'] = False response_data['message'] = str(e) finally: if response_data['success']: try: active_code = get_active_code(reg_name) send_active_email(reg_name, active_code) except Exception as e: response_data['message'] = '发送激活邮件失败,请稍后重新注册' + str(e) new_user.delete() response_data['goto_url'] = settings.CUR_HOST + 'users/login' response_data['goto_time'] = 5 response_data['next_page'] = "用户登录界面" return render(request, 'users/notice.html', response_data) context = { 'user_form': user_form, 'response_data': response_data, } return render(request, 'users/register.html' , context)

之后是激活部分代码,用户在注册邮箱点击激活邮件后将会交给这个视图函数进行处理。根据解密算法去验证用户的合法性并对用户进行激活处理。

def user_active(request, active_code):
    """ 激活用户 """
    # 加错误处理,避免出错。出错认为激活链接失效
    # 解密激活链接
    key, response_data = 9, {}
    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()
            # 在确认用户账号激活成功后及时创建用户信息表
            UserInfo.objects.create(user=user)
            
 
        response_data['goto_page'] = True
        response_data['message'] = '激活成功,欢迎加入Schnee!'

    except IndexError as e:
        response_data['goto_page'] = False
        response_data['message'] = '激活链接无效'
    
    except Exception as e:
        response_data['goto_page'] = False
        response_data['message'] = str(e)
    
    finally:
        #激活成功就跳转到首页(notice页面有自动跳转功能)
        response_data['goto_url'] = settings.CUR_HOST + 'users/login'
        response_data['goto_time'] = 5
        response_data['next_page'] = "用户登录界面"

        return render(request, 'users/notice.html' , response_data)

两个辅助函数,用于生成激活码以及激活码解密:

# -*- coding:utf-8 -*-
"""encrpypt or decrypt the string"""
 
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 ""

你可能感兴趣的:(个人网站搭建)