Django之验证码(十六)
一、验证码原理
第一次访问GET,后台:
创建一张图片
在图片中写入随机字符串
将图片写到指定文件
打开指定目录文件,读取内容
把生成的验证码保存在session中
通过HttpResponse()把图片反馈给前端
提交POST: 获取用户提交的POST和session的验证码比较。
1.1 先来一个小例子:
urls.py:
url(r'^success/$', views.success), url(r'^login_ajax/$', views.login_ajax), url(r'^login_ajax_check/$', views.login_ajax_check), url(r'^verify_code/$', views.verify_code), url(r'^post_article/$', views.post_article),
views.py :
from PIL import Image, ImageDraw, ImageFont #PIL是python2版本的图像处理库,不过现在用Pillow比PIL强大,是python3的处理库 from django.http import JsonResponse from django.shortcuts import render, redirect import random from io import BytesIO #在python3.x中,StringIO已经在io模块中了,导入方法 #ajax登录视图函数 def login_ajax(request): return render(request,'login_ajax.html') #ajax登录校验回调视图函数 def login_ajax_check(request): uname = request.POST.get('uname') #获取用户输入的用户名和密码 password = request.POST.get('password') vcode = request.POST.get('vcode') #获取用户输入的验证码 vcode_session = request.session.get('verifycode') #获取session中的验证码 #用户名和密码校验 if uname == 'root' and password == '123456' and vcode == vcode_session: #保存用户的登录状态 request.session['isLogin'] = True request.session['uname'] = uname request.session['password'] = password return JsonResponse({'msg': 'ok'}) elif uname != 'root' or password != '123456': return JsonResponse({'msg': 'fail_user'}) elif vcode != vcode_session: return JsonResponse({'msg': 'fail_verify'}) def verify_code(request): #定义变量,用于画面的背景色、宽、高 bgcolor = (random.randrange(20, 100), random.randrange( 20, 100), 255) width = 100 height = 25 #创建画面对象 im = Image.new('RGB', (width, height), bgcolor) #创建画笔对象 draw = ImageDraw.Draw(im) #调用画笔的point()函数绘制噪点 for i in range(0, 100): #噪点绘制的范围 xy = (random.randrange(0, width), random.randrange(0, height)) #噪点的随机颜色 fill = (random.randrange(0, 255), 255, random.randrange(0, 255)) #绘制出噪点 draw.point(xy, fill=fill) #定义验证码的备选值 str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0' #随机选取4个值作为验证码 rand_str = '' for i in range(0, 4): rand_str += str1[random.randrange(0, len(str1))] #构造字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont” # font = ImageFont.truetype('FreeMono.ttf', 23) #这里可能会导致报错找不到这个字体,所以如果报错改为下面的字体 font = ImageFont.truetype('arial.ttf', 23) #构造字体颜色 fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255)) #绘制4个字 draw.text((5, 2), rand_str[0], font=font, fill=fontcolor) draw.text((25, 2), rand_str[1], font=font, fill=fontcolor) draw.text((50, 2), rand_str[2], font=font, fill=fontcolor) draw.text((75, 2), rand_str[3], font=font, fill=fontcolor) #释放画笔 del draw #存入session,用于做进一步验证 request.session['verifycode'] = rand_str """ python2的为 # 内存文件操作 import cStringIO buf = cStringIO.StringIO() """ # 内存文件操作-->此方法为python3的 import io buf = io.BytesIO() #内存文件操作 #将图片保存在内存中,文件类型为png im.save(buf, 'png') #将内存中的图片数据返回给客户端,MIME类型为图片png return HttpResponse(buf.getvalue(), 'image/png') # ajax登录成功视图函数 def success(request): # 用户已经登录 if request.session.get('isLogin'): return render(request, 'sucess.html') else: return redirect('/login_ajax/') # 发帖操作视图函数 def post_article(request): # 获取登录的用户名 uname = request.session.get('uname') # 获取帖子的标题 title = request.POST.get('title') content = request.POST.get('content') return HttpResponse('%s发了一篇名为%s的帖子:%s' % (uname.encode('utf-8').decode('utf-8'), title.encode('utf-8').decode('utf-8'),content.encode('utf-8').decode('utf-8')))
login_ajax.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页面</title> <style> #errorMsg { display: none; color: red; } </style> </head> <body> <div> {% csrf_token %} 用户名:<input type="text" id="uname"><br/> 密 码:<input type="password" id="password"><br/> 验证码:<input type="text" id="vcode"/><br/> <!--直接调用生产图片验证码的视图函数,生产验证码--> <img src="/verify_code/" id="imgvcode"/><br/> <input type="button" value="登录" id="btnLogin"><br/> <div id="errorMsg"></div> </div> <script src="/static/jquery-1.12.4.js"></script> <script> $(function () { $('#btnLogin').click(function () { $('uname').reset; csrf = $('input').val(); uname = $('#uname').val(); password = $('#password').val(); vcode = $('#vcode').val(); //发起ajax请求,注意csrf攻击 $.post('/login_ajax_check/', { 'csrfmiddlewaretoken': csrf, 'uname': uname, 'password': password, 'vcode': vcode, }, function (data) { //获取返回的数据并进行操作 if (data.msg === 'ok') { //登录成功 location.href = '/success/' //跳转到成功页面 } else if (data.msg === 'fail_user') { $('#errorMsg').show().text('亲!用户名或密码错误!') } else if (data.msg === 'fail_verify') { //验证码错误 $('#errorMsg').show().text('亲!验证码错误!') } }) }); }); </script> </body> </html>
sucess.html :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录成功,发帖页面</title> </head> <body> <h2>亲!你登录成功了,欢迎你!!!</h2> <form method="post" action="/post_article/"> {% csrf_token %} 标题:<input type="text" name="title"/><br/> 内容:<textarea name="content"></textarea><br/> <input type="submit" value="发帖"/> </form> </body> </html>
测试一把:
#估计输错密码可以进行输入的验证。但是存在一个问题哈,你再点击验证码图片不发生变化了,还得重新刷新下页面。
#点击发帖之后页面再跳转。
1.2 点击验证码刷新
#上图是美团的验证码功能。
login_ajax.html:
$('#imgvcode').css('cursor', 'pointer').click(function () { //css('cursor', 'pointer')是让鼠标变成小手 $('#imgvcode').attr('src', $("#imgvcode").attr('src')+'?1') //找到验证码的id然后给其src所对应的url值加?1 });
#在原有的基础上增加原有的一段js。
#上图可以看到还是有点问题的,每次url都累加,显然不想这么干。
再改进一把:
login_ajax.html:
<img src="/verify_code/" id="imgvcode"/><br/><span id="yzflush">看不清,换一张</span><br> <!--显示验证码哪里稍微改一下,加个p标签--> ... $('#yzflush').css('cursor', 'pointer').click(function () { srctag = $("#imgvcode").attr('src').split('?')[0] //这个就是取到src的值然后以?为分割,取第一个值,这样就类似于每次重置一下url了。 var myDate = new Date(); $('#imgvcode').attr('src', srctag +'?'+ myDate.getTime()) //然后就是字段拼接了,myDate.getTime()就是把当前时间转换成精确到毫秒,这样保证url不会重复 });