Django实现登录时的图片验证码

Django实现图片验证码

1.验证码的作用

  • 防恶意破解密码:防止,使用程序或机器人恶意去试密码.为了提高用户的体验,用户输入错误以后,才会要求输入验证码.
  • 有效防止注册,以防,使用程序或机器人去无限制注册账号.
  • 防止一些投票网站的恶意刷票

验证码原理

2.任务逻辑

前端功能

  • 点击按钮Ajax调用发送验证码功能
  • 输完验证码后Ajax调用验证功能

后端功能

  • 发送验证码功能
    • 生成指定长度验证码
    • 使用Redis代替session缓存, 存储数据! 设置过期时间(1.60秒内无法重新发送短信,2. 设置短信验证码的有效时间)
  • 验证码检查

以下是用django2.2版本实现的,默认你已经创建好项目,验证码为下图所示样子:

  1. 首先在项目的应用下插入一个生成验证码的文件captcha.py。这是生成验证码的一个文件已经封装好啦,直接调用就可以了。代码如下:
    “”"
    图片验证码
    “”"
    import os
    import random

    from io import BytesIO
    
    from PIL import Image
    from PIL import ImageFilter
    from PIL.ImageDraw import Draw
    from PIL.ImageFont import truetype
    
    
    class Bezier:
        """贝塞尔曲线"""
    
        def __init__(self):
            self.tsequence = tuple([t / 20.0 for t in range(21)])
            self.beziers = {}
    
        def make_bezier(self, n):
            """绘制贝塞尔曲线"""
            try:
                return self.beziers[n]
            except KeyError:
                combinations = pascal_row(n - 1)
                result = []
                for t in self.tsequence:
                    tpowers = (t ** i for i in range(n))
                    upowers = ((1 - t) ** i for i in range(n - 1, -1, -1))
                    coefs = [c * a * b for c, a, b in zip(combinations,
                                                          tpowers, upowers)]
                    result.append(coefs)
                self.beziers[n] = result
                return result
    
    
    class Captcha:
        """验证码"""
    
        def __init__(self, width, height, fonts=None, color=None):
            self._image = None
            self._fonts = fonts if fonts else \
                [os.path.join(os.path.dirname(__file__), 'fonts', font)
                 for font in ['Arial.ttf', 'Georgia.ttf', 'Action.ttf']]
            self._color = color if color else random_color(0, 200, random.randint(220, 255))
            self._width, self._height = width, height
    
        @classmethod
        def instance(cls, width=200, height=75):
            prop_name = f'_instance_{width}_{height}'
            if not hasattr(cls, prop_name):
                setattr(cls, prop_name, cls(width, height))
            return getattr(cls, prop_name)
    
        def _background(self):
            """绘制背景"""
            Draw(self._image).rectangle([(0, 0), self._image.size],
                                        fill=random_color(230, 255))
    
        def _smooth(self):
            """平滑图像"""
            return self._image.filter(ImageFilter.SMOOTH)
    
        def _curve(self, width=4, number=6, color=None):
            """绘制曲线"""
            dx, height = self._image.size
            dx /= number
            path = [(dx * i, random.randint(0, height))
                    for i in range(1, number)]
            bcoefs = Bezier().make_bezier(number - 1)
            points = []
            for coefs in bcoefs:
                points.append(tuple(sum([coef * p for coef, p in zip(coefs, ps)])
                                    for ps in zip(*path)))
            Draw(self._image).line(points, fill=color if color else self._color, width=width)
    
        def _noise(self, number=50, level=2, color=None):
            """绘制扰码"""
            width, height = self._image.size
            dx, dy = width / 10, height / 10
            width, height = width - dx, height - dy
            draw = Draw(self._image)
            for i in range(number):
                x = int(random.uniform(dx, width))
                y = int(random.uniform(dy, height))
                draw.line(((x, y), (x + level, y)),
                          fill=color if color else self._color, width=level)
    
        def _text(self, captcha_text, fonts, font_sizes=None, drawings=None, squeeze_factor=0.75, color=None):
            """绘制文本"""
            color = color if color else self._color
            fonts = tuple([truetype(name, size)
                           for name in fonts
                           for size in font_sizes or (65, 70, 75)])
            draw = Draw(self._image)
            char_images = []
            for c in captcha_text:
                font = random.choice(fonts)
                c_width, c_height = draw.textsize(c, font=font)
                char_image = Image.new('RGB', (c_width, c_height), (0, 0, 0))
                char_draw = Draw(char_image)
                char_draw.text((0, 0), c, font=font, fill=color)
                char_image = char_image.crop(char_image.getbbox())
                for drawing in drawings:
                    d = getattr(self, drawing)
                    char_image = d(char_image)
                char_images.append(char_image)
            width, height = self._image.size
            offset = int((width - sum(int(i.size[0] * squeeze_factor)
                                      for i in char_images[:-1]) -
                          char_images[-1].size[0]) / 2)
            for char_image in char_images:
                c_width, c_height = char_image.size
                mask = char_image.convert('L').point(lambda i: i * 1.97)
                self._image.paste(char_image,
                                  (offset, int((height - c_height) / 2)),
                                  mask)
                offset += int(c_width * squeeze_factor)
    
        @staticmethod
        def _warp(image, dx_factor=0.3, dy_factor=0.3):
            """图像扭曲"""
            width, height = image.size
            dx = width * dx_factor
            dy = height * dy_factor
            x1 = int(random.uniform(-dx, dx))
            y1 = int(random.uniform(-dy, dy))
            x2 = int(random.uniform(-dx, dx))
            y2 = int(random.uniform(-dy, dy))
            warp_image = Image.new(
                'RGB',
                (width + abs(x1) + abs(x2), height + abs(y1) + abs(y2)))
            warp_image.paste(image, (abs(x1), abs(y1)))
            width2, height2 = warp_image.size
            return warp_image.transform(
                (width, height),
                Image.QUAD,
                (x1, y1, -x1, height2 - y2, width2 + x2, height2 + y2, width2 - x2, -y1))
    
        @staticmethod
        def _offset(image, dx_factor=0.1, dy_factor=0.2):
            """图像偏移"""
            width, height = image.size
            dx = int(random.random() * width * dx_factor)
            dy = int(random.random() * height * dy_factor)
            offset_image = Image.new('RGB', (width + dx, height + dy))
            offset_image.paste(image, (dx, dy))
            return offset_image
    
        @staticmethod
        def _rotate(image, angle=25):
            """图像旋转"""
            return image.rotate(random.uniform(-angle, angle),
                                Image.BILINEAR, expand=1)
    
        def generate(self, captcha_text='', fmt='PNG'):
            """生成验证码(文字和图片)"""
            self._image = Image.new('RGB', (self._width, self._height), (255, 255, 255))
            self._background()
            self._text(captcha_text, self._fonts,
                       drawings=['_warp', '_rotate', '_offset'])
            self._curve()
            self._noise()
            self._smooth()
            image_bytes = BytesIO()
            self._image.save(image_bytes, format=fmt)
            return image_bytes.getvalue()
    
    
    def pascal_row(n=0):
        """生成毕达哥拉斯三角形(杨辉三角)"""
        result = [1]
        x, numerator = 1, n
        for denominator in range(1, n // 2 + 1):
            x *= numerator
            x /= denominator
            result.append(x)
            numerator -= 1
        if n & 1 == 0:
            result.extend(reversed(result[:-1]))
        else:
            result.extend(reversed(result))
        return result
    
    
    def random_color(start=0, end=255, opacity=255):
        """获得随机颜色"""
        red = random.randint(start, end)
        green = random.randint(start, end)
        blue = random.randint(start, end)
        if opacity is None:
            return red, green, blue
        return red, green, blue, opacity
    

    并导入支持的字体文件:

  2. 在应用下新建一个util.py的文件用来保存一些常用的工具函数,
    def gen_verify_code(length = 4):
    ‘’‘生成指定长度验证码’’’

        all_chars = '0123456789abcdefghijklmnopqrstuvwxyz'
        return ''.join(random.choices(all_chars,k = length))
    
  3. 在试图函数view.py中定义一个函数生成验证码:
    def captcha(request):
    ‘’‘生成验证码’’’
    code_text = gen_verify_code() # 随机生成4位数字
    request.session[‘code’] = code_text # 在服务器中以session形式保存
    image_data = Captcha.instance().generate(code_text)
    return HttpResponse(image_data, content_type=‘image/png’) # 返回图片对象

  4. 前端代码为




    #验证码的试图函数

5.设置点击随机切换的效果(使用jQuery方法,添加点击事件)

<script>
        $(()=>{
            $('#code').on('click',(evt) =>{
                $(evt.target).attr('src','/polls/captcha/?' + Math.random())
                }
            )
        })
    </script>

6.登陆时进行验证的代码为:

# 获取客户端输入的验证码
code_from_user = request.POST.get('captcha', '0')
# 获取服务器session存储的验证码
code_from_serv = request.session.get('code', '1')
# 忽略大小写进行判断是否一致
if code_from_serv.lower() == code_from_user.lower():
  username = request.POST.get('username')
   password = request.POST.get('password')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jin·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值