Django admin login page verification code: ordinary character and arithmetic verification code

1. Preface

By default, the login interface of django only has the user name and password input box, without additional security protection. If it is used directly without login verification code in the production environment, it is very dangerous, because attackers can constantly try to log in with specific programs until they get the correct login password. Therefore, login verification code must be added to increase the cost of website attack.

2. Customize the login page

2.1 creating django project

Create Django project with pychart. The project structure is as follows:

After creation, remember to run in the directory where manage.py is located:

python manage.py migrate

2.2 downloading plug-ins

First download the verify.js front-end login verification plug-in:

JQuery verification code plug-in verify.js_jQuery House - freely share the plug-in libraries of jQuery, html5 and css3

Extract the following directory structure:

two point three   Copy djano login page template

Create the admin directory in templates (under the manage.py peer directory):

  View the django package location using the following command:

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

Then find contrib\admin\templates\admin\login.html in this directory and copy it to templates/admin.

2.4 introducing verification plug-ins

First, create a static/admin folder under the manage.py peer directory to save the global static files related to management, and then create three directories under this folder: js, css and image.

  Find verify.css in the extracted plug-in folder and copy it to the static/admin/css you just created. Then copy all js files in the plugin folder js folder to static/admin/js. Copy the two sample pictures to static/admin/image:

  Modify the static file settings in settings:

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

In this way, the plug-in can be introduced in the following ways:

<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. Custom login template

In order to flexibly use different verification codes, Django's template inheritance is used here (see Template inheritance ), define a login skeleton template and abstract the verification code into a block, so as to realize flexible replacement.

3.1 define login skeleton template

Open the previously copied login.html, find the location as shown in the figure, and add a verify_code block:

Then override the extrahead block of the basic admin template and introduce the Verify.js plug-in:

  Then change the name of the template to login_base.html as the login skeleton template:

3.2 add verification code

Create a new login.html template in the templates/admin directory as the real page login template:

Overwrite the verify we defined earlier in this template_ Code block:

login.html

{#Inherit basic login template#}
{% extends "admin/login_base.html" %}
{% load i18n static %}


{#Overwrite the verification code block in the basic login template verify_code#}
{% block verify_code %}
    <div class="form-row">
        <label class="required">Verification Code:</label>
        <div id="mpanel2"></div>
    </div>
    <script type="text/javascript">
        $('#mpanel2').codeVerify({
            //1 is the ordinary character verification code and 2 is the arithmetic verification code
            type: 1,
            fontSize: '30px',
            codeLength: 6,
            btnId: 'check-btn',
            ready: function () {
            },
            success: function () {
                alert('Verify match!');
            },
            error: function () {
                alert('Verification code mismatch!');
            }
        });
    </script>
{% endblock %}

effect

  If you find any confusion in the interface style, modify and open verify.js, and find:

Modify the width in the default parameter to 99%. Find again

Modify the variable panelHtml to

  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">Change one</a></div></div>';
            

  Open verify.css and find

   Change it to

.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;
}

Modified effect

  be careful

When debugging a web page, close the network cache first, otherwise the previously cached css file will always be displayed when refreshing the page, resulting in debugging difficulties. chrome closes the network cache and checks disable cache under the network Tab of the debugging window,   So does Edge. Remember to close it after debugging, otherwise it will cost a lot of traffic.

4. Add verification code interference

4.1 security issues

The common verification code of verify.js is very clear and easy to be recognized by OCR, and the content of the verification code can be read through html tags, so it is still very unsafe. Therefore, it is best to change the verification code to draw with canvas:

4.2 modify the verify.js source code

(1) Modify constructor

  (2) Modify 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">Change one</a>' +
                '</div>' +
                '<input type="text" class="varify-input-code" />' +
                '</div>';
......

  (3) Add drawing function

Add randNum, randColor, drawbg, drawLine, drawCircle and drawExpression methods to the Code class:

 //Method of defining Code
    Code.prototype = {
        init: function () {
           ......
        },

        //Load page
        loadDom: function () {
           ......
        },
        /**
         * Generate a random number
         * @param min minimum value
         * @param max Maximum
         * @returns {*}
         */
        ranNum: function (min, max) {
            return Math.random() * (max - min) + min;
        },
        /**
         * Returns a random color to set the color range
         * @param {number} min [Lower color limit]
         * @param {number} max [Upper color limit]
         * @return {string} [Random color]
         */
        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})`;
        },
        //Draw background
        drawBg: function (min, max) {
            // Draw canvas background
            this.ctx.fillStyle = this.ranColor(min, max);
            // fill color
            this.ctx.fillRect(0, 0, this.ctxW, this.ctxH);
        },
        /**
         * Draw interference dots
         * @param {number} num [Number of drawings]
         * @param {number} r [Dot radius]
         * @param {number} min [Lower limit]
         * @param {number} max [Online]
         */
        drawCircle: function (num, r, min, max) {
            for (let i = 0; i < num; i++) {
                // Start drawing (pick up the pen)
                this.ctx.beginPath();
                // context.arc(x,y,r,sAngle,eAngle,counterclockwise);  (draw)
                // The x coordinate of the center of the x circle.
                // The y coordinate of the center of the y circle.
                // r is the radius of the circle.
                // sAngle starting angle, in radians. (the three o'clock position of the circle of the arc is 0 degrees).
                // Eagle end angle in radians.
                // Counter clockwise optional. Specifies whether the drawing should be counterclockwise or clockwise. False = clockwise, true = counterclockwise.
                this.ctx.arc(this.ranNum(0, this.ctxW), this.ranNum(0, this.ctxH), r, 0, 2 * Math.PI);
                // fill color
                this.ctx.fillStyle = this.ranColor(min, max);
                // fill
                this.ctx.fill();
                // Close draw (release pen)
                this.ctx.closePath();
            }
        },

        /**
         * Draw interference line segment
         * @param {number} num [Number of drawings]
         * @param {number} min [Lower limit]
         * @param {number} max [Online]
         */

        drawLine: function (num, min, max) {
            for (let i = 0; i < num; i++) {
                // Start drawing (pick up the pen)
                this.ctx.beginPath();
                // Draw start point
                this.ctx.moveTo(this.ranNum(0, this.ctxW), this.ranNum(0, this.ctxH));
                // Draw end point
                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();
            }
        },
        //Draw an arithmetic expression
        drawExpression: function (expression) {
            const fs = this.randNum(20, 50);
            this.ctx.font = fs + "px Verdana";
            this.ctx.fillStyle = this.randColor(0, 100);
            // X value added to the horizontal coordinate (x)
            // Y value added to the vertical coordinate (y)
            // deviation
            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);
                // Save the state of the drawing
                this.ctx.save();
                // X value added to the horizontal coordinate (x)
                // Y value added to the vertical coordinate (y)
                // deviation
                this.ctx.translate(this.ctxW / expression.length * i + this.ctxW / 20, 0);
                // Change angle
                this.ctx.rotate(this.randNum(-30, 30,) * Math.PI / 180);
                // Textspecifies the text to output on the canvas.
                // x the x coordinate position (relative to the canvas) where the text begins to be drawn.
                // y is the y coordinate position (relative to the canvas) where the text begins to be drawn.
                // maxWidth is optional. The maximum text width allowed, in pixels.
                this.ctx.fillText(expression[i], 0, (this.ctxH + fs) / 2.5, this.ctxW / expression.length);
                // Returns previously saved path States and properties
                this.ctx.restore();
            }

        //Set verification code
        setCode: function () {
           .......

        },

       ......
    };

(4) Modify the verification code generation method setCode

 //Set verification code
        setCode: function () {
            // Empty canvas
            this.ctx.clearRect(0, 0, this.ctxW, this.ctxH);
            //Draw background
            this.drawBg(200, 255);
            //Draw interference lines
            this.drawLine(20, 0, 255);
            //Draw interference dots
            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) {
                //Add common verification code character
                for (let i = 0; i < this.options.codeLength; i++) {
                    //Select a character at random
                    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);
                    // Save the state of the drawing
                    this.ctx.save();
                    // X value added to the horizontal coordinate (x)
                    // Y value added to the vertical coordinate (y)
                    // deviation
                    this.ctx.translate(this.ctxW / this.options.codeLength * i + this.ctxW / 20, 0);
                    // Change angle
                    this.ctx.rotate(this.randNum(-30, 30,) * Math.PI / 180);
                    // Textspecifies the text to output on the canvas.
                    // x the x coordinate position (relative to the canvas) where the text begins to be drawn.
                    // y is the y coordinate position (relative to the canvas) where the text begins to be drawn.
                    // maxWidth is optional. The maximum text width allowed, in pixels.
                    this.ctx.fillText(char, 0, (this.ctxH + fs) / 2.5, this.ctxW / this.options.codeLength);
                    // Returns previously saved path States and properties
                    this.ctx.restore();
                    //Add to the selected verification code
                    this.code_chose += _code_chars[charNum];

                }
            } else {		//Algorithm verification code
                let num1 = Math.floor(Math.random() * this.options.figure);
                let num2 = Math.floor(Math.random() * this.options.figure);
                //Choose an arithmetic at random
                if (this.options.arith === 0) {
                    var tmparith = Math.floor(Math.random() * 3);
                }
                //Sequence to draw
                let code = []
                switch (tmparith) {
                    case 1 :
                        //addition
                        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 :
                        //Subtract to make sure there are no negative numbers in the subtraction
                        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 :
                        //multiplication
                        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) Modify verify.css

/*General verification code*/
.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 effect

(1) Ordinary character verification code

(2) Arithmetic verification code

Tags: Django admin

Posted on Mon, 04 Oct 2021 21:46:09 -0400 by leocon