博客園項目-登錄(驗證碼,ajax提交數據,session和cookie)
前端頁面
{% 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)