关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
前面弄好了登入和登出,现在需要继续完善,添加注册认证功能。
这个注册认证分几个步骤。
首先用户用邮箱注册了,但是没有激活。
接着网站后台发送一个带有激活链接的邮件给用户,用户打开链接即可完成激活步骤。
若不激活,就不可以评论。这样可以很大程度减少垃圾评论。
之前已经写好了前端页面,还需要配置链接和处理响应。打开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;
});到这里就完成了注册认证这个功能。后面就开始修改评论功能,加上用户验证。
另外,特别说明一下。大家参考一下思路和一些实现方法即可。若直接复制粘贴到自己的项目中,很大几率会出错的,毕竟文档结构不同。