验证码的生成和验证

269 阅读1分钟

captcha.py

import os
import io
import string
import random

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
from django.conf import settings

class Captcha:
    CAPTCHA_THRESHOLD = 3  # 需要输入验证码的阈值
    CAPTCHA_EXPIRED = 60 * 5  # 过期时间,second
    CAPTCHA_SIZE = 4  # 长度
    CAPTCHA_ALLOWED_CHARS = string.ascii_lowercase  # 可用字符

    def __init__(self):
        ...

    def gen_captcha_code(self):
        """生成图形验证码代码"""
        return ''.join([random.choice(self.CAPTCHA_ALLOWED_CHARS) for _ in range(self.CAPTCHA_SIZE)])


    def gen_captcha(self):
        """生成图形验证码"""

        font_size = 30
        space = 10
        line_density = 10  # 干扰线密度
        point_density = 1000  # 噪点密度

        width = (font_size + space) * self.CAPTCHA_SIZE + space
        height = font_size + space * 2
        code = self.gen_captcha_code()

        random_color = lambda: random.randint(0, 200), random.randint(0, 200), random.randint(0, 200)

        random_light_color = lambda: random.randint(200, 255), random.randint(200, 255), random.randint(200, 255)

        random_point = lambda: random.randint(0, width), random.randint(0, height)

        # 创建画布
        img = Image.new('RGB', (width, height), random_light_color())
        draw = ImageDraw.Draw(img)

        # 噪点
        for _ in range(point_density):
            draw.point(random_point(), fill=random_color())

        # 干扰线
        for _ in range(line_density):
            draw.line([random_point(), random_point()], fill=random_color())

        # 验证码
        font_path = os.path.join(settings.BASE_DIR, 'administrator/static/administrator/fonts/Vera.ttf')
        font = ImageFont.truetype(font_path, font_size)
        for i in range(settings.CAPTCHA_SIZE):
            draw.text(((font_size + space) * i + space, space / 2), code[i], font=font, fill=random_color())

        # 模糊处理
        # img = img.filter(ImageFilter.BLUR)

        buf = io.BytesIO()
        img.save(buf, 'gif')

        return ''.join(code), buf.getvalue()
    

登录方法

    def login():
        #逻辑忽略
        captcha, img = gen_captcha()
        #将二进制数据转base64传给前端
        data={
            'captcha':base64.b64encode(img).decode('utf-8')
        }
        return jsonResponse(data)

前端 vue

<div class="layui-form-item" id="captcha_box" v-if="showCaptcha" v-cloak>
    <div class="layui-row">
        <div class="layui-col-xs7">
             <label class="layadmin-user-login-icon layui-icon layui-icon-vercode" for="LAY-user-login-vercode"></label>
             <input type="text" name="vercode" placeholder="图形验证码" class="layui-input" v-model="captcha" @keyup.enter="login" />
        </div>
        <div class="layui-col-xs5">
             <div style="margin-left: 10px">
                 <img :src="captcha_src" class="layadmin-user-login-codeimg" @click="login('code')" />
             </div>
         </div>
     </div>
</div>

login: function (str) {
    $.ajax({
        url: login_url,
        type: 'POST',
        dataType: 'json',
        data: {
            adminname: this.username,
            password: this.password,
            captcha: this.captcha,
            keep_logined: this.remember
        },
        success: function (res) {
            if (!res.code) {
                localStorage.setItem('login-id', res.data.admin_id);
                localStorage.setItem('login-user', res.data.nickname);
                localStorage.setItem('login-safety', parseInt(res.data.safety));
                localStorage.setItem('login-method', login_method);
                window.location.href = 'index.html';
            } else {
                if (res.data.captcha) {
                    vue.captcha_src = 'data:image/gif;base64,' + res.data.captcha;
                    vue.showCaptcha = true;
                    if (str !== 'code') layer.msg('错误:' + res.msg, {icon: 2});
                } else {
                    layer.msg('错误:' + res.msg, {icon: 2});
                }
            }
        }
    });
},