天天看點

Django 使用圖檔驗證碼進行登入Day 20/12/30圖檔驗證碼登入

Day 20/12/30

Django 使用圖檔驗證碼進行登入Day 20/12/30圖檔驗證碼登入

圖檔驗證碼登入

生成圖檔驗證碼

pip install pillow
           
# -*- coding = utf-8 -*-
# @Time : 2020/12/29 16:09
# @Author : Chenih
# @File : image_code.py
# @Software : PyCharm

from PIL import Image, ImageDraw, ImageFilter, ImageFont


import random

"""繪制圖檔驗證碼"""
def check_code(width=120, height=30, char_length=5, font_file='kumo.ttf', font_size=28):
    code = []
    img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
    draw = ImageDraw.Draw(img, mode='RGB')

    def rndChar():
        """
        生成随機字母
        :return:
        """
        return chr(random.randint(65, 90))

    def rndColor():
        """
        生成随機顔色
        :return:
        """
        return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))

    # 寫文字
    font = ImageFont.truetype(font_file, font_size)
    for i in range(char_length):
        char = rndChar()
        code.append(char)
        h = random.randint(0, 4)
        draw.text([i * width / char_length, h], char, font=font, fill=rndColor())

    # 寫幹擾點
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())

    # 寫幹擾圓圈
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())

    # 畫幹擾線
    for i in range(5):
        x1 = random.randint(0, width)
        y1 = random.randint(0, height)
        x2 = random.randint(0, width)
        y2 = random.randint(0, height)

        draw.line((x1, y1, x2, y2), fill=rndColor())

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
    return img, ''.join(code)

if __name__ == '__main__':
    image_obj, code = check_code()
	image_obj.show()  # 直接打開
    
    # 2. 寫入檔案
    # img,code = check_code()
    # with open('code.png','wb') as f:
    #     img.save(f,format='png')
 
    # 3. 寫入記憶體
    # from io import BytesIO
    # stream = BytesIO()
    # img.save(stream, 'png')
    # stream.getvalue()
           

圖檔驗證碼路由

驗證碼圖檔的視圖函數

def image_code(request):
    """生成圖檔驗證碼"""
    # 調用函數生成圖檔驗證碼
    image_obj, code = check_code()
    # 将驗證碼字元存入session
    request.session['code'] = code
    request.session.set_expiry(60)

    # 建立一個記憶體空間
    stream = BytesIO()
    # 将生成的圖檔驗證碼儲存在記憶體空間中
    image_obj.save(stream, 'png')

    # 将從記憶體空間擷取的圖檔驗證碼傳給前端
    return HttpResponse(stream.getvalue())
           

頁面顯示

{% extends 'layout/basic.html' %}
{% load static %}

{% block title %} 郵箱|手機号 登入 {% endblock %}


{% block css %}
    <link rel="stylesheet" href="{% static 'css/account.css' %}">
    <style>
        .error-msg {
            color: red;
            position: absolute;
            font-size: 13px;
        }
    </style>
{% endblock %}


{% block content %}
    <div class="account">
        <div class="title">郵箱|手機号 登入</div>
        <form id="smsForm" method="POST" novalidate>
            {% csrf_token %}
            {% for field in form %}
                {% if field.name == 'code' %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        <div class="row">
                            <div class="col-xs-7">
                                {{ field }}
                                <span class="error-msg">{{ field.errors.0 }}</span>
                            </div>
                            <div class="col-xs-5">
                                {# 從image_code中拿到生成的圖檔 #}
                                <img src="{% url 'image_code' %}" id="imageCode" title="點選更換圖檔" alt="">
                            </div>
                        </div>
                    </div>
                {% else %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {{ field }}
                        <span class="error-msg">{{ field.errors.0 }}</span>
                    </div>
                {% endif %}
            {% endfor %}

        <div>
            <div style="float: right">
                <a href="{% url 'loginsms' %}">短信驗證碼登入?</a>
            </div>
        </div>

            <div class="row">
                <div class="col-xs-3">
                    <input type="submit" class="btn btn-primary" value="登  錄"/>
                </div>
            </div>
        </form>
    </div>
{% endblock %}

{% block js %}
    <script>
        $(function () {
            $('#imageCode').click(function () {
                var oldSrc = $(this).attr('src');
                $(this).attr('src', oldSrc + '?');
            })
        })
    </script>
{% endblock %}
           

驗證碼圖檔的點選更換:

給圖檔<img src="{% url 'image_code' %}" id="imageCode" title="點選更換圖檔" alt="">設定id,平且給此id添加點選事件。

{% block js %}
    <script>
        $(function () {
            // 找到id為imageCode,添加點選事件
            $('#imageCode').click(function () {
                // 定義變量oldSrc,拿到目前id的src屬性的值(圖檔位址)
                var oldSrc = $(this).attr('src');
                // 每點選一次就将目前id所在的src的值加一個?(用來替換圖檔)
                // oldSrc ->  oldSrc? -> oldSrc?? -> ...
                $(this).attr('src', oldSrc + '?');
            })
        })
    </script>
{% endblock %}
           

登入

def login(request):
    """使用者名密碼登入"""
    if request.method == 'GET':
        form = LoginForm(request)
        return render(request, 'login.html', {'form': form})
    
    form = LoginForm(request, data=request.POST)
    if form.is_valid():
        # 擷取資料時寫的name為username,實際上擷取的是手機号或者郵箱
        username = form.cleaned_data['username']  
        password = form.cleaned_data['password']

        # user_obj = models.User.objects.filter(username=username, password=password).first()
        # 使用Q對象構造複雜的查詢對象(判斷是用email還是phone登入的)
        user_obj = models.User.objects.filter(Q(email=username) | Q(phone=username)).filter(password=password).first()

        if user_obj:
            # 如果此使用者存在,那麼将使用者的id儲存在session中
            request.session['user_id'] = user_obj.pk
            request.session.set_expiry(60*60*24*14)
            return redirect('index')
        # 若不存在此使用者,則抛出錯誤提示
        form.add_error('username', '使用者名或密碼有誤')
    return render(request, 'login.html', {'form': form})