栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

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

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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__)

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

2.4 引入验证插件

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

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

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

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

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


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 %}
    
        
        
    
    
{% endblock %}

效果

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

修改默认参数中的width为99%。 再找到

修改变量panelHtml为

  var panelHtml = '换一张';
            

 打开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 = '' +
                '' +
                '' +
                '换一张' +
                '' +
                '' +
                '';
......

 (3)添加绘制函数

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

 //定义Code的方法
    Code.prototype = {
        init: function () {
           ......
        },

        //加载页面
        loadDom: function () {
           ......
        },
        
        ranNum: function (min, max) {
            return Math.random() * (max - min) + min;
        },
        
        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);
        },
        
        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();
            }
        },

        

        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 () {
            // 清空canvas
            this.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)算术验证码

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/293520.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号