1. 程式人生 > >博客園項目-登錄(驗證碼,ajax提交數據,session和cookie)

博客園項目-登錄(驗證碼,ajax提交數據,session和cookie)

doc orm valid 對象 導入 [] fse pan ()

前端頁面

技術分享圖片
{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <link rel="stylesheet" href="{% static ‘bootstrap-3.3.7/css/bootstrap.min.css‘ %}">
    <style>
        body {
            background-color: #eeeeee;
        }
        h3{
            padding-left: 100px;
        }
        .container{
            margin-top: 300px;
        }
    </style>
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h3>請登錄</h3>
            <form class="form-horizontal">
                {% csrf_token %}
                <div class="form-group">
                    <label for="inputUser" class="col-sm-2 control-label">賬號</label>
                    <div class="col-sm-10">
                        <input type="text" class="form-control" id="inputUser" placeholder="賬號">
                        <span class="help-block"></span>
                    </div>
                </div>
                <div class="form-group">
                    <label for="inputPassword" class="col-sm-2 control-label">密碼</label>
                    <div class="col-sm-10">
                        <input type="password" class="form-control" id="inputPassword" placeholder="Password">
                        <span class="help-block"></span>
                    </div>
                </div>

                <div class="form-group">
                    <label for="valid_code" class="col-sm-2 control-label">驗證碼</label>
                    <div class="col-sm-5">
                        <input type="text" class="form-control" id="valid_code" placeholder="驗證碼">
                        <span class="help-block"></span>
                    </div>
                    <div class="col-sm-5">
                        <img src="/get_valid_img/"  width="200" height="40">
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <input type="button" class="btn btn-primary btn-lg btn-block" value="登錄">
                        <span></span>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

<script src="{% static ‘jquery-3.2.1.min.js‘ %}"></script>
<script src="{% static ‘bootstrap-3.3.7/js/bootstrap.min.js‘ %}"></script>
<script>

    $(":button").click(function () {
        $.ajax({
            url:"/login/",
            type:"post",
            data:{
                "user":$("#inputUser").val(),
                "pwd":$("#inputPassword").val(),
                "valid_code": $("#valid_code").val(),
                "csrfmiddlewaretoken":$("[name=‘csrfmiddlewaretoken‘]").val()
            },
            success: function (data) {
                var data = JSON.parse(data);
                if (data.is_login){
                    location.href="/index/"
                }else{
                    $(":button").next().text(data.error_msg).css("color","red")
                    setTimeout(function () {
                        $(":button").next().text("")
                    },1000)
                }
            }
        })
    })

</script>
</body>
</html>
技術分享圖片

可以看到用戶訪問登錄頁面時,在求情驗證碼圖片時會訪問/get_valid_img/,通過該url來得到驗證碼圖片

用戶的數據通過ajax提交到後端,當後端完成驗證後,如果驗證成後,則跳轉到首頁,如果失敗,則將錯誤信息添加到頁面上

生成隨機驗證碼

技術分享圖片
def get_valid_img(request):
    # from utils.random_code import get_random_code  # 局部模塊導入不能用*
    # data = get_random_code()
    import random

    def get_random_color():
        return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))


    # 方式1:
    # f = open("car.jpg", "rb")
    # data = f.read()
    # f.close()

    # 方式2:將圖片存到磁盤
    # import PIL
    # from PIL import Image
    # from io import BytesIO
    # image = Image.new(mode="RGB", size=(120, 80), color=get_random_color())
    # f = open("code.png", "wb")
    # image.save(f, "png")
    # f.close()
    # f = open("code.png", "rb")
    # data = f.read()
    # f.close()

    # 方式3:將圖片放到內存
    # import PIL
    # from PIL import Image
    # from io import BytesIO
    # image = Image.new(mode="RGB", size=(120, 80), color=get_random_color())
    # f = BytesIO()
    # image.save(f, "png")
    #
    # data = f.getvalue()

    # 方式4:
    from PIL import Image  # PIL模塊可以通過下載pillow模塊使用
    from io import BytesIO
    from PIL import ImageDraw, ImageFont  # 畫筆
    image = Image.new(mode="RGB", size=(120, 80), color=get_random_color())
    draw = ImageDraw.Draw(image)
    font = ImageFont.truetype("static/fonts/kumo.ttf", size=32)
    temp = []
    for i in range(5):
        random_char = random.choice(
            [chr(random.randint(65, 90)), chr(random.randint(97, 122)), str(random.randint(0, 9))])
        draw.text((i * 24, 20), random_char, get_random_color(), font=font)  # 參數分別為坐標,添加的字符串,顏色,字體
        temp.append(random_char)
        # 為了讓生成的驗證碼更具迷惑性,我們可以使用下面的方法給驗證碼圖片中增加隨機的點線等
    # for i in range(80):
    #     draw.point((random.randint(0,width),random.randint(0,height)),fill=get_random_color())
    #
    # for i in range(10):
    #     x1=random.randint(0,width)
    #     x2=random.randint(0,width)
    #     y1=random.randint(0,height)
    #     y2=random.randint(0,height)
    #     draw.line((x1,y1,x2,y2),fill=get_random_color())
    # for i in range(40):
    #     draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
    #     x = random.randint(0, width)
    #     y = random.randint(0, height)
    #     draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
    f = BytesIO()
    image.save(f, "png")

    data = f.getvalue()
    # 保存隨機字符串
    random_code_str = "".join(temp)
    request.session["random_code_str"] = random_code_str
    ‘‘‘
    1 生成隨機字符串
    2 響應set_cookie {"sessionid": "e21e12gg2d3sds"}
    3 在django_session表中插入一條記錄
        session_key     session_data
    ‘‘‘

    return HttpResponse(data)
技術分享圖片

由於驗證碼需要生成圖片,此處我們導入了PIL模塊(用來生成圖片的模塊)

先生成一個image對象,圖片的顏色應該是隨機的,我們定義一個生成3個隨機數字組成元組的函數

import random

    def get_random_color():
        return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

驗證碼中還需要有隨機的字符串,此時我們從PIL模塊中導入ImageDraw(畫筆), ImageFont(字體)

首先生成一個字體對象,要先下載字體文件,並存放到項目中

技術分享圖片

然後通過該字體文件生成字體對象

font = ImageFont.truetype("static/fonts/kumo.ttf", size=32)  # size為字體大小

然後往生成image圖片中添加內容,內容應該為隨機字符串,采用以下方法生成並添加到image圖片中

技術分享圖片
draw = ImageDraw.Draw(image)  # 畫筆
font = ImageFont.truetype("static/fonts/kumo.ttf", size=32)
temp = []
for i in range(5):
    random_char = random.choice(
        [chr(random.randint(65, 90)), chr(random.randint(97, 122)), str(random.randint(0, 9))])
    draw.text((i * 24, 20), random_char, get_random_color(), font=font)
    temp.append(random_char)
技術分享圖片

由於驗證碼圖片只是發送給用戶用來驗證用的,沒有必要保存到本地磁盤中,我們可以從io模塊中導入BytesIO,在內存中生成空間來存放圖片

from io import BytesIO
f = BytesIO()
image.save(f, "png")

data = f.getvalue()

此時將data返回給頁面,用戶就可以看到驗證碼圖片了

但是為了方便我們接收到用戶輸入的驗證碼後進行驗證,我們需要保存此次生成驗證碼的內容

# 保存隨機字符串
random_code_str = "".join(temp)

現在我們需要考慮一個問題,用上面的方法保存驗證碼內容,如果一個用戶先訪問了登錄頁面,此時random_code_str被賦予了一個值,而這是另一個用戶也訪問了登錄頁面,這時random_code_str會被後一個值覆蓋

那麽前一個用戶輸入正確的驗證碼也無法驗證成功了

在這種情況下,我們需要保證每一個用戶都能保存住自己訪問頁面時驗證碼的值,可以考慮使用session

# 保存隨機字符串
random_code_str = "".join(temp)
request.session["random_code_str"] = random_code_str

這裏我們需要知道request.session做了什麽:

1 生成隨機字符串
2 響應set_cookie {"sessionid": "e21e12gg2d3sds"}
3 在django_session表中插入一條記錄

如果用戶訪問時,已經有了cookie,那麽這個操作會更新數據庫django_session表中session_data的內容,而不會改變session_key等其它內容

點擊更新驗證碼

當我們登錄時,經常會遇到驗證碼看不清的情況,此時我們可以通過點擊驗證碼圖片進行刷新

這個功能可以通過前端JS實現

    // 驗證碼刷新
    $("img").click(function () {
        $(this)[0].src += "?"
    })

找到驗證碼圖片的img標簽,給他綁定一個點擊事件,通過DOM對象的方法,在該圖片的src後面加一個?號便可實現再次訪問

後端驗證

技術分享圖片
def log_in(request):
    response = {"is_login": False, "error_msg": ""}
    if request.method == "POST":
        username = request.POST.get("user")
        pwd = request.POST.get("pwd")
        valid_code = request.POST.get("valid_code")
        code_str = request.session.get("random_code_str")
        if valid_code.upper() == code_str.upper():
            user = auth.authenticate(username=username, password=pwd)
            if user:
                response["is_login"] = True
                auth.login(request, user)  # 1 {"user_id": 1} 2 request.user=user
            else:
                response["error_msg"] = "用戶名密碼錯誤"
        else:
            response["error_msg"] = "驗證碼錯誤"
        return HttpResponse(json.dumps(response))
    return render(request, "login.html")
技術分享圖片

我們先校驗驗證碼是否正確,在驗證碼正確的前提下再使用auth組件進行用戶名和密碼的驗證

需要註意,當用戶名密碼也驗證成功後,我們使用auth.login()方法寫session,但由於此前訪問驗證碼圖片時已經在數據庫中生成了session記錄,該方法會先flush清除記錄,再添加新的記錄

所以這時我們會看到cookie中的sessionid的值變了,這裏要和生成驗證碼時使用的request.session方法區分開來

博客園項目-登錄(驗證碼,ajax提交數據,session和cookie)