1.前言

django的登录界面默认只有用户名和密码输入框,没有额外的安全防护,如果在生产环境中不加登录验证码直接使用,是非常危险的,因为攻击者可以用特定程序不断的进行登录尝试,直至得出正确的登录密码,所以必须加上登录验证码,提高网站攻击成本。

2. 自定义登录页面

2.1 创建django项目

使用Pycharm创建Django项目,项目结构如下:

创建完成后记得在manage.py所在目录运行:

python manage.py migrate

2.2 下载插件

首先下载verify.js前端登录验证插件:

jquery验证码插件verify.js_jQuery之家-自由分享jQuery、html5、css3的插件库

解压得到以下目录结构:

2.3 复制djano登录页面模板

templates(manage.py同级目录下)创建admin目录:

使用以下命令查看django包位置:

 python -c "import django; print(django.__path__)

然后在这个目录下找到contrib\admin\templates\admin\login.html,将其复制到templates/admin下。

2.4 引入验证插件

首先在manage.py同级目录下创建static/admin文件夹,用来保存与管理有关的全局静态文件,然后这个文件夹下创建三个目录:jscssimage

在解压后的插件文件夹中找到verify.css,将其复制到刚刚创建的static/admin/css下。然后将插件文件夹js文件夹下的所有js文件复制到static/admin/js下。将两张示例图片复制到static/admin/image下:

修改setttings中的静态文件设置:

STATICFILES_DIRS = [BASE_DIR / "static","static/admin"
]

这样插件可以通过下面的方式引入:

<link rel="stylesheet" href="{% static "css/verify.css"%}">
<script type="text/javascript" src="{% static "js/jquery-1.11.0.min.js"%}"></script>
<script type="text/javascript" src="{% static "js/verify.js" %}"></script>

3.自定义login模板

为了能灵活使用不同的验证码,这里使用Django的模板继承(参见模板继承),定义一个登录骨架模板,把验证码抽象为一个块,从而实现灵活替换。

3.1 定义登录骨架模板

打开之前复制的login.html,找到如图所示位置,添加一个verify_code块:

然后覆盖基础admin模板的extrahead块,引入Verify.js插件:

然后将这个模板的名字改为login_base.html,作为登录骨架模板:

3.2 添加验证码

templates/admin目录下新建一个login.html模板,作为真正的页面登录模板:

在这个模板中覆盖我们之前定义的verify_code块:

login.html

{#继承基础登录模板#}
{% extends "admin/login_base.html" %}
{% load i18n static %}{#覆盖基础登录模板中的验证码块verify_code#}
{% block verify_code %}<div class="form-row"><label class="required">验证码:</label><div id="mpanel2"></div></div><script type="text/javascript">$('#mpanel2').codeVerify({//1为普通字符验证码,2为算数验证码type: 1,fontSize: '30px',codeLength: 6,btnId: 'check-btn',ready: function () {},success: function () {alert('验证匹配!');},error: function () {alert('验证码不匹配!');}});</script>
{% endblock %}

效果

 发现界面样式有错乱的地方,修改打开verify.js,找到:

修改默认参数中的width99%。 再找到

修改变量panelHtml

  var panelHtml = '<div class="cerify-code-panel"><div class="verify-code"></div><div class="verify-code-area"><input type="text" class="varify-input-code" /><a class="verify-change-code">换一张</a></div></div>';

打开verify.css,找到

将其修改为

.cerify-code-panel {height: 100%;overflow: hidden;
}.verify-code-area {width: 100%;justify-items: stretch;
}
.varify-input-code {width: 80%;height: 25px;
}.verify-change-code {width: 20%;color: #337AB7;cursor: pointer;margin-left: 10px;text-align: center;
}

修改后效果

注意 

调试网页时要先关闭网络缓存,否则刷新页面总是显示之前缓存的css文件,导致调试困难。chrome关闭网络缓存在调试窗口网络Tab下勾选禁用缓存, Edge也一样。调试完记得关闭,不然很费流量。

4. 增加验证码干扰

4.1 安全性问题

verify.js的普通验证码很清晰,容易被OCR识别,并且验证码的内容可以通过html标签读取,因此还是很不安全,所以最好将验证码改成使用canvas绘制:

4.2 修改verify.js源码

(1)修改构造函数

(2)修改loadDom

 const panelHtml = '<div class="vertify-code-panel">' +'<div class="verify-code-area">' +'<canvas class="verify-code" width="200" height="60"  id="verify-code"></canvas>' +'<a class="verify-change-code">换一张</a>' +'</div>' +'<input type="text" class="varify-input-code" />' +'</div>';
......

(3)添加绘制函数

Code类中添加randNum、randColor、drawbg、drawLine、drawCircle、drawExpression这几个方法:

 //定义Code的方法Code.prototype = {init: function () {......},//加载页面loadDom: function () {......},/*** 产生一个随机数* @param min 最小值* @param max 最大值* @returns {*}*/ranNum: function (min, max) {return Math.random() * (max - min) + min;},/*** 返回一个随机颜色 可设置颜色区间* @param {number} min [颜色下限]* @param {number} max [颜色上限]* @return {string} [随机颜色]*/ranColor: function (min, max) {const r = this.ranNum(min, max);const g = this.ranNum(min, max);const b = this.ranNum(min, max);return `rgb(${r},${g},${b})`;},//绘制背景drawBg: function (min, max) {// 绘制canvas背景this.ctx.fillStyle = this.ranColor(min, max);// 填充颜色this.ctx.fillRect(0, 0, this.ctxW, this.ctxH);},/*** 绘制干扰 圆点* @param {number} num [绘制的数量]* @param {number} r [圆点半径]* @param {number} min [下限]* @param {number} max [上线]*/drawCircle: function (num, r, min, max) {for (let i = 0; i < num; i++) {// 开始绘制 (拿起笔)this.ctx.beginPath();// context.arc(x,y,r,sAngle,eAngle,counterclockwise); (绘制)// x 圆的中心的 x 坐标。// y 圆的中心的 y 坐标。// r 圆的半径。// sAngle 起始角,以弧度计。(弧的圆形的三点钟位置是 0 度)。// eAngle 结束角,以弧度计。// counterclockwise 可选。规定应该逆时针还是顺时针绘图。False = 顺时针,true = 逆时针。this.ctx.arc(this.ranNum(0, this.ctxW), this.ranNum(0, this.ctxH), r, 0, 2 * Math.PI);// 填充颜色this.ctx.fillStyle = this.ranColor(min, max);// 填充this.ctx.fill();// 闭合绘制 (放开笔)this.ctx.closePath();}},/*** 绘制干扰 线段* @param {number} num [绘制的数量]* @param {number} min [下限]* @param {number} max [上线]*/drawLine: function (num, min, max) {for (let i = 0; i < num; i++) {// 开始绘制 (拿起笔)this.ctx.beginPath();// 绘制开始点this.ctx.moveTo(this.ranNum(0, this.ctxW), this.ranNum(0, this.ctxH));// 绘制结束点this.ctx.lineTo(this.ranNum(0, this.ctxW), this.ranNum(0, this.ctxH));this.ctx.strokeStyle = this.ranColor(min, max);this.ctx.stroke();this.ctx.closePath();}},//绘制算数表达式drawExpression: function (expression) {const fs = this.randNum(20, 50);this.ctx.font = fs + "px Verdana";this.ctx.fillStyle = this.randColor(0, 100);// x 添加到水平坐标(x)上的值// y 添加到垂直坐标(y)上的值// 偏移for (let i = 0; i < expression.length; i++) {const fs = this.randNum(20, 50);this.ctx.font = fs + "px Verdana";this.ctx.fillStyle = this.randColor(0, 100);// 保存绘制的状态this.ctx.save();// x 添加到水平坐标(x)上的值// y 添加到垂直坐标(y)上的值// 偏移this.ctx.translate(this.ctxW / expression.length * i + this.ctxW / 20, 0);// 变换角度this.ctx.rotate(this.randNum(-30, 30,) * Math.PI / 180);// text 规定在画布上输出的文本。// x 开始绘制文本的 x 坐标位置(相对于画布)。// y 开始绘制文本的 y 坐标位置(相对于画布)。// maxWidth 可选。允许的最大文本宽度,以像素计。this.ctx.fillText(expression[i], 0, (this.ctxH + fs) / 2.5, this.ctxW / expression.length);// 返回之前保存过的路径状态和属性this.ctx.restore();}//设置验证码setCode: function () {.......},......};

(4)修改验证码生成方法setCode

 //设置验证码setCode: function () {// 清空canvasthis.ctx.clearRect(0, 0, this.ctxW, this.ctxH);//绘制背景this.drawBg(200, 255);//绘制干扰线条this.drawLine(20, 0, 255);//绘制干扰圆点this.drawCircle(20, 5, 200, 255);const color1Num = Math.floor(Math.random() * 3);const color2Num = Math.floor(Math.random() * 5);this.htmlDoms.code.css({'background-color': _code_color1[color1Num], 'color': _code_color2[color2Num]});this.htmlDoms.code_input.val('');this.code_chose = '';if (this.options.type === 1) {//添加普通验证码字符for (let i = 0; i < this.options.codeLength; i++) {//随机选中一个字符const charNum = Math.floor(Math.random() * 52);let char = _code_chars[charNum]const fs = this.randNum(20, 50);this.ctx.font = fs + "px Verdana";this.ctx.fillStyle = this.randColor(0, 100);// 保存绘制的状态this.ctx.save();// x 添加到水平坐标(x)上的值// y 添加到垂直坐标(y)上的值// 偏移this.ctx.translate(this.ctxW / this.options.codeLength * i + this.ctxW / 20, 0);// 变换角度this.ctx.rotate(this.randNum(-30, 30,) * Math.PI / 180);// text 规定在画布上输出的文本。// x 开始绘制文本的 x 坐标位置(相对于画布)。// y 开始绘制文本的 y 坐标位置(相对于画布)。// maxWidth 可选。允许的最大文本宽度,以像素计。this.ctx.fillText(char, 0, (this.ctxH + fs) / 2.5, this.ctxW / this.options.codeLength);// 返回之前保存过的路径状态和属性this.ctx.restore();//添加到选定的验证码中this.code_chose += _code_chars[charNum];}} else {       //算法验证码let num1 = Math.floor(Math.random() * this.options.figure);let num2 = Math.floor(Math.random() * this.options.figure);//随机选择一种算数if (this.options.arith === 0) {var tmparith = Math.floor(Math.random() * 3);}//要绘制的序列let code = []switch (tmparith) {case 1 ://加法this.code_chose = parseInt(String(num1)) + parseInt(String(num2));code.push(String(num1))code.push("+")code.push(String(num2))code.push("=")code.push('?')this.drawExpression(code)break;case 2 ://减法, 确保减法不出现负数if (parseInt(String(num1)) < parseInt(String(num2))) {var tmpnum = num1;num1 = num2;num2 = tmpnum;}this.code_chose = parseInt(String(num1)) - parseInt(String(num2));code.push(String(num1))code.push("-")code.push(String(num2))code.push("=")code.push('?')this.drawExpression(code)break;default ://乘法this.code_chose = parseInt(String(num1)) * parseInt(String(num2));code.push(String(num1))code.push("×")code.push(String(num2))code.push("=")code.push('?')this.drawExpression(code)break;}}},

(5)修改verify.css

/*常规验证码*/
.verify-code {text-align: center;cursor: pointer;border: 1px solid #ddd;
}.verify-code-panel {height: 100%;overflow: hidden;
}.verify-code-area {width: 100%;display: flex;justify-items: stretch;justify-content: flex-start;align-items: center;flex-direction: row;margin-bottom: 5px;
}.varify-input-code {width: 100%;height: 25px;
}.verify-change-code {width: 20%;color: #337AB7;cursor: pointer;margin-left: 10px;text-align: center;
}
......

4.3 效果

(1)普通字符验证码

(2)算术验证码

Django admin登录页面验证码(1):普通字符和算术验证码相关推荐

  1. 为 Django admin 登录页添加验证码

    为什么80%的码农都做不了架构师?>>>    历史原因,使用上古版本 django 1.6.5,但新版本应该大同小异 首先添加自定义后台模块app, 如adm,并添加到 INSTA ...

  2. java验证码验证码_Java登录页面实时验证用户名密码和动态验证码

    ●登录名和密码是同时验证的,并不是先验证登录名是否存在,然后再验证密码是否正确,是同时进行验证,若登录名和密码当中一个条件不符合,则提示用户登录名或者密码错误, 这样做的意义是为了保证用户信息的安全( ...

  3. Java登录页面实时验证用户名密码和动态验证码

    ●登录名和密码是同时验证的,并不是先验证登录名是否存在,然后再验证密码是否正确,是同时进行验证,若登录名和密码当中一个条件不符合,则提示用户登录名或者密码错误, 这样做的意义是为了保证用户信息的安全( ...

  4. django admin 登录用户名密码错误提示

    项目的登录页是前同事自定义的,登录时用户名和密码错误时没有提示,体验不太好. 所以想加一个提示,刚开始看的django-suit的login.html源码没搞对,又看了一下django的login.h ...

  5. php编写用户登录页面,PHP实现用户登录页面

    PHP学习日常,放在上面记录一下咯 我用了bootstrap框架,这样的界面要好看一点 登录页面: 必须用户名.密码.验证码都输入正确才能登录成功喔,否则出现下面提示 登陆成功之后,登录和注册选项切换 ...

  6. 设计登录页面测试用例,页面包括账号、密码和验证码,账号字符长度小于20,密码字符长度小于30,验证码数字和字母组合,长度为4

    1. 正确输入账号.密码和验证码,登录成功. 2. 账号为空,应提示"请输入账号". 3. 账号长度为19个字符,符合要求. 4. 账号长度为20个字符,符合要求. 5. 账号长度 ...

  7. JavaEE之--登录页面(用户名、密码、验证码)

    1.登陆界面(简易制作,丑勿喷): login.jsp: <%@ page contentType="text/html;charset=UTF-8" language=&q ...

  8. 用来向登录页面输出验证码图片的一般处理程序页面

    这是自己以前做的B/S项目中的一个输出验证码图片的页面,没什么技术含量,希望高手们不要嘲笑,只是希望为需要帮助的人尽一点绵薄之力罢了! 页面简介:该页面是一个以ashx 为后缀的一般处理程序页面,用于 ...

  9. Django admin后台管理页面的常用设置

    Django admin后台管理页面的常用设置 选择列表选项choices # filename: models.pyfrom django.db import models# 例1 int类型:ST ...

最新文章

  1. CornerNet代码解析——损失函数
  2. 深度学习之反向传播算法
  3. SAP PP ECR的Profile规定了用它可以修改哪些数据对象
  4. Java的List排序
  5. CNN 模型的参数(parameters)数量和浮点运算数量(FLOPs)是怎么计算的
  6. android 网络图片查看器,Handler的用法
  7. 【最佳解法】剑指 Offer 42. 连续子数组的最大和
  8. java phantomjs 截图_phantomjs 截图
  9. s7-1200跟mysql_让西门子S7-1200直接连接MySQL数据库!!!
  10. java导入数据 neo4j_java-neo4j-使用neo4j剩余图数据库进行批量插...
  11. 高通在物联网领域已经深耕多年
  12. java客户端服务器聊天程序流程图_基于java的socket简单聊天编程
  13. oracle的采购管理模块,ORACLEERP采购管理模块操作手册
  14. 市场主流单片机的介绍和选择指南
  15. headfirstjava 学习笔记(chapter1-5)
  16. YOLOX测试-VOC格式
  17. 使用Python及SMTP协议发送邮件(以163邮箱为例)
  18. MATLAB 学习笔记(6)MATLAB 的 upsample 函数和 downsample 函数
  19. 去除桌面图标蓝底的方法
  20. OS学习笔记-3(清华大学慕课)系统启动流程及中断处理

热门文章

  1. 让代码看起来更舒服(1):选择适合的配色方案(更新配色方案生成器)
  2. 飞桨实战营-图像分割学习笔记和心得体会
  3. 循环神经网络(RNN)简易教程
  4. MAC上可以用的连接安卓、苹果手机的全功能助手-魔方手机助手
  5. 记录网件r6220路由器登录配置
  6. MG996R 舵机内部驱动电路原理图和拆解实物图
  7. 《网络营销实战密码:策略 技巧 案例》书评
  8. 基于PLC控制的导热油温控系统如何实现远程监控
  9. cp abe java_CP-ABE基于密文策略的属性加密(JAVA源码)
  10. 经典案例----磁盘容量获取错误