关于本站
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; });
到这里就完成了注册认证这个功能。后面就开始修改评论功能,加上用户验证。
另外,特别说明一下。大家参考一下思路和一些实现方法即可。若直接复制粘贴到自己的项目中,很大几率会出错的,毕竟文档结构不同。