验证码的存在是为了防止系统被暴力破解攻击,几乎每个系统都有验证码。下面将介绍三种生成验证码方式。
您可以根据你自己的需要进行学习。
手动生成验证码
安装绘图依赖,利用的是画图模块 PIL
以及随机模块 random
在后台生成一个图片和一串随机数,然后保存在内存中。
pip install pillow
编写跨图工具
code.py
注意需要指定 Monaco.ttf 字体 ,我这里直接放在项目根目录:font_file='Monaco.ttf'
import random from PIL import Image, ImageDraw, ImageFont, ImageFilter def check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28): code = [] img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') def rndChar(): """ 生成随机字母 :return: """ # return str(random.randint(0, 9)) return chr(random.randint(65, 90)) def rndColor(): """ 生成随机颜色 :return: """ return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255)) # 写文字 font = ImageFont.truetype(font_file, font_size) for i in range(char_length): char = rndChar() code.append(char) h = random.randint(0, 4) draw.text([i * width / char_length, h], char, font=font, fill=rndColor()) # 写干扰点 for i in range(40): draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) # 写干扰圆圈 for i in range(40): draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) x = random.randint(0, width) y = random.randint(0, height) draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor()) # 画干扰线 for i in range(5): x1 = random.randint(0, width) y1 = random.randint(0, height) x2 = random.randint(0, width) y2 = random.randint(0, height) draw.line((x1, y1, x2, y2), fill=rndColor()) img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) return img, ''.join(code) if __name__ == '__main__': img, code_str = check_code() print(code_str)
编写视图函数
views.py
LoginForm类:配置登录表单,涵盖用户名、密码和验证码。返回给模板HTML用于渲染页面。
image_code 函数:调用pillow函数,生成图片,设置60秒写入到自己的session中(以便于后续获取验证码再进行校验)
login函数:验证码这块代码主要是校验从前端传过来的验证码是否跟存在session中的验证码一致,如果一直则继续执行下面代码。否则抛出验证码错误异常。
class LoginForm(BootstrapForm): username = forms.CharField( label="用户名", widget=forms.TextInput, required=True ) password = forms.CharField( label="密码", widget=forms.PasswordInput(render_value=True), required=True, ) code = forms.CharField( label="验证码", widget=forms.TextInput, required=True ) def image_code(request): """ 生成图片验证码 """ # 调用pillow函数,生成图片 img, code_string = check_code() # 写入到自己的session中(以便于后续获取验证码再进行校验) request.session['image_code'] = code_string # 给Session设置60s超时 request.session.set_expiry(60) stream = BytesIO() img.save(stream, 'png') return HttpResponse(stream.getvalue()) def login(request): if request.method == 'GET': form = LoginForm() return render(request, 'login.html', {'form': form}) form = LoginForm(data = request.POST) if form.is_valid(): # 验证码的校验 user_input_code = form.cleaned_data.pop('code') code = request.session.get('image_code', "") if code.upper() != user_input_code.upper(): form.add_error("code", "验证码错误") return render(request, 'login.html', {'form': form}) # 查询数据库匹配用户名密码是否正确 user_obj = models.UserInfo.objects.filter(**form.cleaned_data).first() print(user_obj) if not user_obj: form.add_error('password', '用户名或密码错误') return render(request, 'login.html', {'form': form}) request.session['info'] = {'id': user_obj.id, 'username': user_obj.username} # 设置 session 过期时间为30分钟 request.session.set_expiry(60 * 30) return redirect('/user/list/') return render(request, 'login.html', {'form': form})
login.html
{{ form.code }}:验证码。
{{ form.code.errors.0 }}:验证码输出错误时返回错误提示。
在 img 标签设置 onclick事件,当用户单击验证码图片生成一个新的验证码。
相当于向服务器发送请求:http://localhost:8000/image/code/?temp=随机数
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用户管理系统</title> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"> <style> .account { width: 400px; border: 1px solid #dddddd; border-radius: 5px; box-shadow: 5px 5px 20px #aaa; margin-left: auto; margin-right: auto; margin-top: 100px; padding: 20px 40px; } .account h2 { margin-top: 10px; text-align: center; } </style> </head> <body> <div class="account"> <h2>用户登录</h2> <form method="post" novalidate> {% csrf_token %} <div class="form-group"> <label>用户名</label> {{ form.username }} <span style="color: red;">{{ form.username.errors.0 }}</span> </div> <div class="form-group"> <label>密码</label> {{ form.password }} <span style="color: red;">{{ form.password.errors.0 }}</span> </div> <div class="form-group"> <label for="id_code">图片验证码</label> <div class="row"> <div class="col-xs-7"> {{ form.code }} <span style="color: red;">{{ form.code.errors.0 }}</span> </div> <div class="col-xs-5"> <img id="image_code" src="/image/code/" onclick="refreshImg(this)" style="width: 125px;" /> </div> </div> </div> <input type="submit" value="登 录" class="btn btn-primary"> </form> </div> </body> <script> function refreshImg(ths) { ths.src = '/image/code/?temp=' + Math.random(); } </script> </html>
配置路由
path('login/', views.login), path('image/code/', views.image_code),
编辑
总结
- 画图程序
check_code.py
保存在项目任意位置即可,只需在视图函数中导入即可。 Monaco.ttf
字体不可或缺,放置在静态文件中即可,但是需要修改check_code.py
中的字体引入路径。- 验证用户输入的验证码是否正确,只需从 session 中取出生成的验证码与其比较即可。
- 验证码刷新,只需让其再发送一次 get 请求即可。
自动生成验证码
1.安装 django-simple-captcha模块
pip install django-simple-captcha
2.注册到django容器
在 settings.py添加以下内容
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'ums.apps.UmsConfig', 'captcha', ]
更新数据库表,
3.添加路由
在 urls.py
中添加 captcha
对应的路由
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('captcha', include('captcha.urls')), # 验证码 ]
4.修改Form表单
Django 中通常都是由 Form 生成表单,而验证码一般也伴随注册登录表单,因此需要在 forms.py
中添加验证码的字段。
from django import forms from captcha.fields import CaptchaField # 一定要导入这行 class UserForm(forms.Form): username = forms.CharField( label='用户名', # 在表单里表现为 label 标签 max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}) # 添加 css 属性 ) captcha = CaptchaField( label='验证码', required=True, error_messages={ 'required': '验证码不能为空' } )
5.编写视图函数
from django.shortcuts import render from app.forms import UserForm def index(request): register_form = UserForm(request.POST) if register_form.is_valid(): pass register_form = UserForm() return render(request, 'index.html', {'register_form': register_form})
6.编写模板
<html> <head></head> <body> <form action='#' method='post'> {{ register_form.captcha.label_tag }} {{ register_form.captcha }} {{ </form> </body> </html>
总结
使用django-simple-captcha第三方库会生成验证码并存储到自带的captcha表中。
滑动验证码
官方下载源码包,并安装 geetest
模块
gt3-python-sdk
文件。
pip install geetest pip install requests # 有可能还需要 requests 模块 <!-- 引入封装了failback的接口--initGeetest --> <script src="http://static.geetest.com/static/tools/gt.js"></script>
编写模板template
login.html
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.css' %}"> <style> .login-col { margin-top: 100px; } </style> </head> <body> <div class="container"> <div class="row"> <div class="well col-md-6 col-md-offset-3 login-col"> <h3 class="text-center">登录</h3> <form> {% csrf_token %} <div class="form-group"> <label for="username">用户名:</label> <input type="text" class="form-control" id="username" placeholder="用户名" name="username"> </div> <div class="form-group"> <label for="password">密码:</label> <input type="password" class="form-control" id="password" placeholder="密码" name="password"> </div> <!--极验科技滑动验证码--> <div class="form-group"> <!-- 放置极验的滑动验证码 --> <div id="popup-captcha"></div> </div> <!--记住我--> <div class="checkbox"> <label> <input type="checkbox"> 记住我 </label> </div> <!--登录按钮--> <button type="button" class="btn btn-primary btn-block" id="login-button">提交</button> <!--错误信息--> <span class="login-error"></span> </form> </div> </div> </div> </body> </html>
JS 代码主要分为两部分,
第一部分是获取表单的 value 值,向后台发送 Ajax 请求,以验证用户名及密码是否正确,若有错误将错误信息显示出来。
第二部分向后台获取验证码所需相关参数。
<script src="{% static 'js/jquery-3.3.1.js' %}"></script> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.js' %}"></script> <!-- 引入封装了failback的接口--initGeetest --> <script src="http://static.geetest.com/static/tools/gt.js"></script> <script> var handlerPopup = function (captchaObj) { // 成功的回调 captchaObj.onSuccess(function () { var validate = captchaObj.getValidate(); var username = $('#username').val(); var password = $('#password').val(); console.log(username, password); $.ajax({ url: "/accounts/login2/", // 进行二次验证 type: "post", dataType: 'json', data: { username: username, password: password, csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), geetest_challenge: validate.geetest_challenge, geetest_validate: validate.geetest_validate, geetest_seccode: validate.geetest_seccode }, success: function (data) { console.log(data); if (data.status) { // 有错误,在页面上显示 $('.login-error').text(data.msg); } else { // 登录成功 location.href = data.msg; } } }); }); // 当点击登录按钮时,弹出滑动验证码窗口 $("#login-button").click(function () { captchaObj.show(); }); // 将验证码加到id为captcha的元素里 captchaObj.appendTo("#popup-captcha"); // 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html }; $('#username, #password').focus(function () { // 将之前的错误清空 $('.login-error').text(''); }); // 验证开始需要向网站主后台获取id,challenge,success(是否启用failback) $.ajax({ url: "/accounts/pc-geetest/register?t=" + (new Date()).getTime(), // 加随机数防止缓存 type: "get", dataType: "json", success: function (data) { // 使用initGeetest接口 // 参数1:配置参数 // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件 initGeetest({ gt: data.gt, challenge: data.challenge, product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效 offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注 // 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config
视图函数 views.py
from django.shortcuts import render, redirect, HttpResponse from django.http import JsonResponse from geetest import GeetestLib def login2(request): if request.method == 'POST': ret = {'status': False, 'msg': None} username = request.POST.get('username') password = request.POST.get('password') print(username, password) # 获取极验,滑动验证码相关参数 gt = GeetestLib(pc_geetest_id, pc_geetest_key) challenge = request.POST.get(gt.FN_CHALLENGE, '') validate = request.POST.get(gt.FN_VALIDATE, '') seccode = request.POST.get(gt.FN_SECCODE, '') status = request.session[gt.GT_STATUS_SESSION_KEY] user_id = request.session["user_id"] print(gt, challenge, validate, seccode, status) if status: result = gt.success_validate(challenge, validate, seccode, user_id) else: result = gt.failback_validate(challenge, validate, seccode) if result: # 验证码正确 # 利用auth模块做用户名和密码的校验 user_obj = auth.authenticate(username=username, password=password) if user_obj: # 用户名密码正确 # 给用户做登录 auth.login(request, user_obj) ret["msg"] = "/accounts/home/" # return redirect('accounts:home') else: # 用户名密码错误 ret["status"] = True ret["msg"] = "用户名或密码错误!" else: ret["status"] = True ret["msg"] = "验证码错误" return JsonResponse(ret) return render(request, "accounts/login2.html") # 请在官网申请ID使用,示例ID不可使用 pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c" pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4" # 处理极验 获取验证码的视图 def get_geetest(request): user_id = 'test' gt = GeetestLib(pc_geetest_id, pc_geetest_key) status = gt.pre_process(user_id) request.session[gt.GT_STATUS_SESSION_KEY] = status request.session["user_id"] = user_id response_str = gt.get_response_str() return HttpResponse(response_str)
配置路由
from django.urls import path from accounts import views app_name = 'accounts' urlpatterns = [ path('home/', views.home, name='home'), # 极验滑动验证码 获取验证码的url path('pc-geetest/register/', views.get_geetest, name='get_geetest'), path('login2/', views.login2, name='login2'), ]
编辑
总结
- 画图程序
check_code.py
保存在项目任意位置即可,只需在视图函数中导入即可。 Monaco.ttf
字体不可或缺,放置在静态文件中即可,但是需要修改check_code.py
中的字体引入路径。- 验证用户输入的验证码是否正确,只需从 session 中取出生成的验证码与其比较即可。
- 验证码刷新,只需让其再发送一次 get 请求即可。
需要源代码评论区可以给我留言!
如果本文对你有帮助,记得点赞关注,你的支持是我最大的动力!