乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

  • AIOps

  • Python

    • 基础知识

    • Django框架

      • Django之框架
      • Django之ORM详解
      • Django之操作ORM
      • Django之操作ORM例子
      • Django之路由系统
      • Django之跨站请求伪造
      • Django之Cookie和Session
      • Django之CBV和FBV
      • Django之中间件
      • Django之用户认证系统
      • Django之Form组件
      • Django之Ajax
      • Django之模板语言
      • Django之URL分发
      • Django中的缓存
      • Models迁移原理
      • 自定义组件Xadmin
      • Markdown 富文本插件
      • 手写一个验证码
      • 其他

    • DevOps

    • 专栏
    • Python
    • Django框架
    乔克
    2025-07-19
    目录

    手写一个验证码

    我们经常在登录一个网站,或者注册的时候需要输入一个验证码,有时候觉得很烦,因为有些验证码不仅复杂还看不清,许多用户就会因为这些而懒得再登录或者注册之类的。

    既然验证码会造成流失用户的风险,为什么大家都还要使用验证码呢?

    这是验证码在一定程度上起到保护网站安全的作用,比如防止大规模恶意注册(比如手机验证码形式,一机一户),再比如反爬虫(至少不会轻易让你爬取数据)等,你看用户基数最大的 12306,就会有各种验证码。

    既然验证码这么重要,它的原理是什么?是怎么实现的?

    它的原理其实很简单,就是在服务器端生成验证码,发送给客户端,并以图像格式显示。客户端提交所显示的验证码,服务端接收并进行比较,若比对失败则不能实现登录或注册,反之成功后跳转相应界面。

    我们知道了其原理,实现起来就很简单了,现在网络上也有各种各样已经做好的验证码,我们完全可以拿来即用。但是为了更好的理解其原理,我们来手撸一个简单的验证码,以下是在 Django 中实现。

    # 简单的验证码

    (1)、我们在 urls.py 中定义一条路由,如下:

    url(r'getcode', views.get_code, name="get_code"),
    
    1

    (2)、我们定义一个视图函数 get_code(),如下:

    from io import BytesIO
    
    from PIL import Image, ImageDraw, ImageFont
    from django.conf import settings
    from django.shortcuts import render, HttpResponse, redirect
    def get_code(request):
        """
        手撸一个验证码
        """
        # 定义图像颜色模型
        mode = "RGB"
        # 定义图像尺寸
        size = (200, 100)
        # 定义背景色
        bg_color = (255, 0, 0)
        # 创建图像
        image = Image.new(mode=mode, size=size, color=bg_color)
        # 创建画布
        image_draw = ImageDraw.Draw(image, mode=mode)
        # 创建字体,第一个参数是字体,第二个参数是字体大小
        image_font = ImageFont.truetype(settings.FONT_PATH, 100)
        # 创建一个验证码
        verify_code = "Joke"
        # 生成验证码
        fill_color = (255,255,255)
        for i in range(4):
            image_draw.text(xy=(50 * i, 0), text=verify_code[i], font=image_font, fill=fill_color)
        # 保存图像
        fp = BytesIO()
        image.save(fp, "png")
        return HttpResponse(fp.getvalue(), content_type="image/png")
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

    其中 settings.FONT_PATH 是我预先定义好的字段,如下

    STATICFILES_DIRS = [    os.path.join(BASE_DIR, "statics"),]
    FONT_PATH = os.path.join(os.path.join(STATICFILES_DIRS[0], "fonts"),"constan.ttf")
    
    1
    2

    然后我们我们启动服务 python manager.py runserver,在浏览器上就可以看到验证码生成了

    7707e14c72570078bedb03b5cb1aefa3 MD5

    功能是实现了,但是我们现在是自定义了一个验证码字段,我们是需要随机生成验证码,而且字体颜色,背景颜色这些也不要定死了,然后我们再生成一些干扰点,我们对代码进行如下重构:

    def get_code(request):
        """
        手撸一个验证码
        """
        # 定义图像颜色模型
        mode = "RGB"
        # 定义图像尺寸
        size = (200, 100)
        # 定义背景色
        bg_color = (get_color(), get_color(), get_color())
        # 创建图像
        image = Image.new(mode=mode, size=size, color=bg_color)
        # 创建画布
        image_draw = ImageDraw.Draw(image, mode=mode)
        # 创建字体,第一个参数是字体,第二个参数是字体大小
        image_font = ImageFont.truetype(settings.FONT_PATH, 100)
        # 创建一个验证码
        # verify_code = "Joke"
        verify_code = get_verify_code()
        # 生成验证码
        # fill_color = (255,255,255)
        for i in range(4):
            fill_color = (get_color(),get_color(),get_color())
            image_draw.text(xy=(50 * i, 0), text=verify_code[i], font=image_font, fill=fill_color)
        # 加入干扰点
        for i in range(10000):
            fill_color = (get_color(),get_color(),get_color())
            xy = (random.randrange(200), random.randrange(100))
            image_draw.point(xy=xy,fill=fill_color)
        # 保存图像
        fp = BytesIO()
        image.save(fp, "png")
        return HttpResponse(fp.getvalue(), content_type="image/png")
    
    
    def get_color():
        """随机获取颜色"""
        return random.randrange(256)
    
    
    def get_verify_code():
        """随机获取验证码"""
        verify_code = ''.join(random.choice(string.ascii_uppercase + string.digits + string.ascii_lowercase) for x in range(4))
        return verify_code
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44

    然后我们重启应用,刷新页面如下

    4a9edadf509de4d7dcb746522a0203a7 MD5

    是不是有点神似了?下面我们创建一个简单的 login 页面,来实际应用一下验证码。

    # 简单的登录页面

    (1)、创建一个路由

    url(r'login',views.login, name="login"),
    
    1

    (2)、创建一个 Login 的视图函数

    def login(request):
        """登录页面"""
        if request.method == "POST":
            pass
        return render(request, "login.html")
    
    1
    2
    3
    4
    5

    (3)、创建一个 login.html 的 template

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
    </head>
    <body>
    <form action="{% url 'app01:login' %}" method="post">
    	{% csrf_token %}
        <span>用户名:</span><input type="text" name="username">
        <br>
        <span>验证码:</span><input type="text" name="verify_code">
        <br>
        <img src="{% url 'app01:get_code' %}" alt="">
        <br>
        <button>登录</button>
    </form>
    
    </body>
    </html>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    然后重启服务,浏览器访问如下

    cd46372fa92105fcc5fed751629dedbf MD5

    现在我们只是简单的搭建起了流程,我们需要的功能还没有实现,我们需要的功能有:

    1、验证码校验

    2、点击图片自动刷新验证码

    3、忽略大小写

    我们现在对项目进行重构,如下:

    (1)、我们在 get_code 视图函数添加一行代码,如下

    # 创建一个验证码
        # verify_code = "Joke"
        verify_code = get_verify_code()
    # 加入session
        request.session['verify_code'] = verify_code
        ......
    
    1
    2
    3
    4
    5
    6

    (2)、修改 login 视图函数,如下

    def login(request):
        """登录页面"""
        if request.method == "POST":
            storage_code = request.session.get("verify_code")
            submit_code = request.POST.get("verify_code")
            if storage_code.lower() == submit_code.lower():
                return HttpResponse("登录成功")
        return render(request, "login.html")
    
    1
    2
    3
    4
    5
    6
    7
    8

    (3)、修改 login.html 代码如下

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Login</title>
    </head>
    <body>
    <form action="{% url 'app01:login' %}" method="post">
        {% csrf_token %}
        <span>用户名:</span><input type="text" name="username">
        <br>
        <span>验证码:</span><input type="text" name="verify_code">
        <br>
        <img src="{% url 'app01:get_code' %}" alt="" name="verify_image">
        <br>
        <button>登录</button>
    </form>
    <script src="https://cdn.bootcss.com/jquery/3.2.0/jquery.js"></script>
    <script>
        $(function () {
            $("img").on("click", function () {
                console.log("来了啊")
                $(this).attr("src","{% url 'app01:get_code' %}"+"?id="+Math.random())
            })
        })
    </script>
    </body>
    </html>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    然后就可以愉快的玩耍了,是不是很简单呢?

    作者:乔克

    本文链接:https://jokerbai.com

    版权声明:本博客所有文章除特别声明外,均采用 署名-非商业性-相同方式共享 4.0 国际 (CC-BY-NC-SA-4.0) 许可协议。转载请注明出处!

    上次更新: 2025/07/19, 11:33:23
    Markdown 富文本插件
    Python代码规范

    ← Markdown 富文本插件 Python代码规范→

    最近更新
    01
    使用 Generic Webhook Trigger 触发 Jenkins 多分支流水线自动化构建
    07-19
    02
    使用Zadig从0到1实现持续交付平台
    07-19
    03
    基于Jira的运维发布平台
    07-19
    更多文章>
    Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式