The codes implemented Django

1. django-simple-captcha module

  1. installation django-simple-captcha
pip install django-simple-captcha
pip install Pillow
  1. registered

And registered the same app, captcha also needs to be registered settingsin. And it will also create your own data table, it is also necessary data synchronization.

# settings.py
INSTALLED_APPS = [
    ...
    'captcha',
]

# 执行命令进行数据迁徙,会发现数据库中多了一个 captcha_captchastore 的数据表
python manage.py migrate
  1. Add Route

Under the project root directory urls.pyto add the captchacorresponding routing:

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('captcha', include('captcha.urls')),        # 验证码
]
  1. Modify Form Form

Django are usually generated by the Form form, and generally accompanied by verification code registration login form, and therefore need to forms.pyadd a field verification code.

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': '验证码不能为空'
        }
    )
  1. View function:
from django.shortcuts import render
from app.forms import UserForm


def home(request):
    register_form = UserForm(request.POST)
    if register_form.is_valid():
        pass
    register_form = UserForm()
    return render(request, 'index.html', {'register_form': register_form})
  1. Front-end rendering

The next step is how to render the front end:

<html>
    <head></head>
    <body>
        <form action='#' method='post'>
            {{ register_form.captcha.label_tag }}
            {{ register_form.captcha }} {{ 
        </form>
    </body>
</html>

So long

2. manually generated codes

The main use is drawing module PILand a random module randomto generate a string of random numbers and images in the background, and then stored in memory (can also be stored directly in the Django project).

In the front end of a specified imgtag, which src attribute path: generating path verification code <img src='/accounts/check_code/'.

  1. Paint program check_code.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大写字母
_numbers = ''.join(map(str, range(3, 10)))  # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))

# PIL

def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="static/font/Monaco.ttf",
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance=2):
    """
    @todo: 生成验证码图片
    @param size: 图片的大小,格式(宽,高),默认为(120, 30)
    @param chars: 允许的字符集合,格式字符串
    @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
    @param mode: 图片模式,默认为RGB
    @param bg_color: 背景颜色,默认为白色
    @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
    @param font_size: 验证码字体大小
    @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
    @param length: 验证码字符个数
    @param draw_lines: 是否划干扰线
    @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
    @param draw_points: 是否画干扰点
    @param point_chance: 干扰点出现的概率,大小范围[0, 100]
    @return: [0]: PIL Image实例
    @return: [1]: 验证码图片中的字符串
    """

    width, height = size  # 宽高
    # 创建图形
    img = Image.new(mode, size, bg_color)
    draw = ImageDraw.Draw(img)  # 创建画笔

    def get_chars():
        """生成给定长度的字符串,返回列表格式"""
        return random.sample(chars, length)

    def create_lines():
        """绘制干扰线"""
        line_num = random.randint(*n_line)  # 干扰线条数

        for i in range(line_num):
            # 起始点
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            # 结束点
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        """绘制干扰点"""
        chance = min(100, max(0, int(point_chance)))  # 大小限制在[0, 100]

        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        """绘制验证码字符"""
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars)  # 每个字符前后以空格隔开

        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                  strs, font=font, fill=fg_color)

        return ''.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 图形扭曲参数
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params)  # 创建扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜,边界加强(阈值更大)

    return img, strs

Tips

Here you need to specify Monaco.ttffont:

font_type="static/font/Monaco.ttf",

# https://pan.baidu.com/s/1XwyaFC_MROFA4fXujVwH3A 提取码:17f8 
  1. View function views.py
from django.shortcuts import render, redirect, HttpResponse
from blog.check_code import create_validate_code
from io import BytesIO
from django.contrib import auth
from django.http import JsonResponse


def check_code(request):
    """
    获取验证码
    :param request:
    :return:
    """
    stream = BytesIO()
    # 生成图片 img、数字代码 code,保存在内存中,而不是 Django 项目中
    img, code = create_validate_code()
    img.save(stream, 'PNG')

    # 写入 session
    request.session['valid_code'] = code
    print(code)
    return HttpResponse(stream.getvalue())


def login(request):
    """
    登录视图
    :param request:
    :return:
    """
    if request.method == 'POST':
        ret = {'status': False, 'message': None}
        username = request.POST.get('username')
        password = request.POST.get('password')

        # 获取用户输入的验证码
        code = request.POST.get('check_code')
        p = request.POST.get('p')


        # 用户输入的验证码与 session 中取出的验证码比较
        if code.upper() == request.session.get('valid_code').upper():
            # 验证码正确,验证用户名密码是否正确
            user_obj = auth.authenticate(username=username, password=password)
            if user_obj:
                # 验证通过,则进行登录操作
                # 封装到 request.user 中
                auth.login(request, user_obj)
                return redirect('accounts:home')
            else:
                ret['status'] = True
                ret['message'] = '用户名或密码错误'
                return render(request, 'accounts/login.html', ret)
        else:
            ret['status'] = True
            ret['message'] = '验证码错误'
        return render(request, 'accounts/login.html', ret)

    return render(request, 'accounts/login.html')
  1. log in page 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>

            <!--错误信息-->
            {% if status %}
                <div class="alert alert-danger" role="alert">
                    <p id="login-error">{{ message }}</p>
                    <p id="login-error"></p>
                </div>
            {% endif %}

            <form action="{% url 'accounts:login' %}" method="post" novalidate>
                {% csrf_token %}
                <div class="form-group">
                    <label for="exampleInputUsername">用户名:</label>
                    <input type="text" class="form-control" id="exampleInputUsername" placeholder="用户名" name="username">
                </div>

                <div class="form-group">
                    <label for="exampleInputPassword1">密码:</label>
                    <input type="password" class="form-control" id="exampleInputPassword" placeholder="密码"
                           name="password">
                </div>

                <!--验证码-->
                <div class="form-group">
                    <label for="id_code">验证码:</label>
                    <div class="row">
                        <div class="col-md-7 col-xs-7">
                            <input type="text" class="form-control" id="id_code" placeholder="请输入验证码" name="check_code">
                        </div>

                        <div class="col-md-5 col-xs-5">
                            <img src="/accounts/check_code" onclick="changeImg(this)" class="img">
                        </div>
                    </div>
                </div>


                <div class="checkbox">
                    <label>
                        <input type="checkbox"> 记住我
                    </label>
                </div>
                <button type="submit" class="btn btn-primary btn-block" id="login-button">提交</button>
            </form>
        </div>
    </div>
</div>

<script src="{% static 'js/jquery-3.1.1.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.js' %}"></script>

<script>
    function changeImg(ths) {
        // 硬编码
        ths.src = '/accounts/check_code/?temp=' + Math.random();

        // 使用命名空间,发送请求
        // ths.src = '{% url 'accounts:check_code' %}' + '?temp=' + Math.random();

    }

</script>

</body>
</html>

To the captcha imglabel binding onclickevent, when the user clicks the verification code, which is equivalent access http://127.0.0.1:8000/accounts/check_code/?temp=一个随机数, ie http://127.0.0.1:8000/accounts/check_code/send a get request, generates a verification code again and returns from the background.

  1. routing accounts/urls.py
from django.urls import path
from accounts import views

app_name = 'accounts'
urlpatterns = [
    # 登录
    path('login/', views.login, name='login'),      

    # 获取验证码
    path('check_code/', views.check_code, name='check_code'),

    # 首页
    path('home/', views.home, name='home'),

    # 注销
    path('logout/', views.logout, name='logout'),
]

Tips

  • Paint check_code.pystored anywhere on the item, in view of the need to import function.
  • Monaco.ttfFont indispensable, can be placed in a static document, but you need to modify check_code.pythe font path is introduced.
  • Validate the user entered PIN is correct, simply remove generated from the session codes to their comparison.
  • Security code Refresh, and then just let it send a request to get.

3. Technology of the sliding pole test code

In addition to the above two pictures verification code, there is a slide code, used more have extremely inspection technology .

  1. The official download source, and install geetestthe module

Official website

Visit the official website, choose: Technical Documentation - behavior verification - to select the server to deploy Python- use git directly or download gt3-python-sdkfiles.

pip install geetest
pip install requests    # 有可能还需要 requests 模块

<!-- 引入封装了failback的接口--initGeetest -->
<script src="http://static.geetest.com/static/tools/gt.js"></script>
  1. log in page login2.html

html part

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

JavaScript section

<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
            }, handlerPopup);
        }
    });

</script>

JS code is divided into two parts, the first is to obtain a form of value value, send an Ajax request to the background, in order to verify the user name and password are correct, the error will be displayed if an error message. Obtaining a second portion of codes required parameters to the background.

  1. View function 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)
  1. routing accounts/urls.py
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'),
]

to sum up

  • Posterior pole sliding codes in addition to supporting Django, supports flask, tornado, etc.
  • The above post sent in the form of Ajax request, so pay attention to see whether a csrf_token, and the submit button buttonto submit the type should be buttonrather than submit(stepped hole)
  • It also has an embedded, mobile terminal, etc., more examples, please refer to the official download source.

Guess you like

Origin www.cnblogs.com/midworld/p/10992019.html