为了防止机器人频繁登录网站或者破坏分子恶意登录,很多用户登录和注册系统都提供了图形验证码功能。

在Django中实现图片验证码功能非常简单,有现成的第三方库可以使用,我们不必自己开发,比如其中一个库Pillow。

安装Pillow

执行命令pip install pillow

Pillow

  1. Image画布
    主要负责尺寸,颜色,模式
  2. ImageDraw画笔
    属于哪个画布,主要负责文字,点,线,弧
  3. ImageFont字体
    画布的辅助工具,负责字体和样式

绘制流程

  • background = (10, 20, 30) RGB颜色
  • 初始化画布image = Image.new(‘RGB’, (100, 50), background)
  • 获取画布中画笔对象
    • draw = ImageDraw.Draw(image)
  • 绘制验证码,随机四个
    • font = ImageFont.truetype(‘path’, size)
    • Fontcolor = (20, 40, 60)
    • Draw.text((x, y), ‘R’, font, fontcolor)

内存流

  • 内存流BytesIO()
  • 将图片存在内存流中,从流中获取到数据值
  • 通过HttpResponse返回图片内容
    • content_type
    • MIME
      • 表示打开数据的应用程序
      • image/png
1
2
3
4
# 字节流
fp = BytesIO()
image.save(fp, "png")
return HttpResponse(fp.getvalue(), content_type="image/png")

完整代码如下:

  1. 在urls.py中添加
    1
    2
    3
    urlpatterns = [
    path('getcode/', views.get_code, name='get_code'),
    ]
  2. 创建utils.py,写入数据处理的函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import random

    def get_color():
    return random.randrange(256)

    def generate_code():
    source = "1234567890qwertyuiopasdfghjklmnbvcxzZXCVBNMLKJHGFDSAQWERTYUIOP"
    code = ""
    for i in range(4):
    code += random.choice(source)
    return code
  3. 在views.py
    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
    def get_code(request):
    # 初始化画布, 初始化画笔
    mode = "RGB"
    size = (200, 100)
    red = get_color()
    green = get_color()
    blue = get_color()
    color_bg = (red, green, blue)

    image = Image.new(mode=mode, size=size, color=color_bg)
    imagedraw = ImageDraw(image, mode=mode)
    imagefont = ImageFont.truetype('Hiragino Sans GB.ttc', size=80)
    # 随机字符
    verify_code = generate_code()

    # request.session['verify_code'] = verify_code
    for i in range(4):
    fill = (get_color(), get_color(), get_color())
    imagedraw.text(xy=(50*i, 0), text=verify_code[i], font=imagefont, fill=fill)

    # 干扰点
    for i in range(2000):
    fill = (get_color(), get_color(), get_color())
    xy = (random.randrange(201), random.randrange(100))
    imagedraw.point(xy=xy, fill=fill)
    # 字节流
    fp = BytesIO()
    image.save(fp, "png")
    return HttpResponse(fp.getvalue(), content_type="image/png")

验证码验证

  • 客户端验证
  • 服务端验证
    • 在验证码生成时存储验证码
    • 在提交时验证有效性
  1. 在login.html中加入验证码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    {% load static %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Login</title>
    </head>
    <body>
    <form action="{% url 'app:login' %}" method="post">
    <span>用户名:</span><input type="text" name="username" placeholder="请输入用户名">
    <br>
    <span>验证码:</span><input type="text" name="verify_code" placeholder="请输入验证码">
    <img src="{% url 'app:get_code' %}" alt="验证码" >
    <br>
    <button>登陆</button>
    </form>
    </body>
    </html>
  2. 在views.py加入存储
    1
    2
    def get_code(request):
    request.session['verify_code'] = verify_code
  3. 登陆校验
    比较存储的验证码和输入的验证码是否一致,这里利用lower()不区分大小写
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @csrf_exempt
    def login(request):
    if request.method == "GET":
    return render(request, 'login.html')
    elif request.method == "POST":
    receive_code = request.POST.get("verify_code")
    store_code = request.session.get("verify_code")
    if receive_code.lower() != store_code.lower():
    return redirect(reverse('app:login'))
    return HttpResponse("POST请求成功")

验证码刷新

  • 浏览器缓存策略
    • 以url为标识
    • 解决方案
      • 每次不同地址
      • 对地址进行参数拼接不同参数
  1. 创建static文件夹,并在创建子文件夹js,创建login.js。这里用的是jquery写法。
    1
    2
    3
    4
    5
    6
    $(function () {
    $("img").click(function () {
    console.log("点到我了")
    $(this).attr("src", '/app/getcode/?t=' + Math.random())
    })
    })
  2. 导入
    1
    2
    <script type="text/javascript" src="https://cdn.bootcss.com/jquery/1.11.1/jquery.js"></script>
    <script type="text/javascript" src="{% static 'js/login.js'%}"></script>