from django.contrib import admin from django.urls import path from blog import views urlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), ]
建立login視圖函數javascript
from django.shortcuts import render # Create your views here. def login(request): return render(request, 'login.html')
login.html:css
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陸頁面</title> <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css"> </head> <body> <h3>登陸頁面</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-lg-offset-3"> <form action=""> <div class="form-group"> {# label標籤的"for"屬性可把label綁定到另一個元素,所以要把for屬性值與input的id屬性相同。#} {# 當用戶選擇該標籤時,瀏覽器就會自動將焦點轉到和標籤相關的表單控件 #} <label for="user">用戶名</label> {# 這裏不必加name屬性了,以前加是點擊submit按鈕本身組裝鍵值發出去,如今用ajax發只要找到標籤拿到裏面的值便可 #} <input type="text" id="user" class="form-control"> </div> <div class="form-group"> <label for="pwd">密碼</label> <input type="password" id="pwd" class="form-control"> </div> {# 這裏提交按鈕不能使用<input type="submit">這就變成form表單提交事件了。 button類型時,這個按鈕沒有任何事件,能夠給這個按鈕綁定一個事件 #} <input type="button" class="btn btn-default login-btn pull-right" value="提交"> </form> </div> </div> </div> </body> </html>
注意:html
一、label標籤的"for"屬性可把label綁定到另一個元素,所以要把for屬性值與input的id屬性相同。當用戶選擇該標籤時,瀏覽器就會自動將焦點轉到和標籤相關的表單控件;前端
二、這裏不必加name屬性了,以前加是點擊submit按鈕本身組裝鍵值發出去,如今用ajax發只要找到標籤拿到裏面的值便可。java
三、這裏提交按鈕不能使用<input type="submit">這樣就變成form表單提交事件了。 設爲button類型時,這個按鈕沒有任何事件,能夠給這個按鈕綁定一個事件python
<div class="container"> <div class="row"> <div class="col-md-6 col-lg-offset-3"> <form action=""> <div class="form-group"> <label for="user">用戶名</label> <input type="text" id="user" class="form-control"> </div> <div class="form-group"> <label for="pwd">密碼</label> <input type="password" id="pwd" class="form-control"> </div> <div class="form-group"> <label for="pwd">驗證碼</label> <div class="row"> <div class="col-md-6"> <input type="text" class="valid_code form-control"> </div> <div class="col-md-6"> {# src還能夠設置請求路徑 #} <img width="270" height="40" src="/get_validCode_img/" alt=""> </div> </div> </div> <input type="button" class="btn btn-default login-btn pull-right" value="提交"> </form> </div> </div> </div>
注意:<img src="">,src除了能夠指定圖片路徑仍是設置爲請求路徑。jquery
path('get_validCode_img/', views.get_validCode_img),
def get_validCode_img(request): with open("lufei.jpg", "rb") as f: data = f.read()
顯示效果以下:web
不推薦使用這種方法,驗證圖片不能僅僅指定一張圖片,這種方法把程序寫死了。ajax
生成動態隨機圖片,使用Python圖像處理庫:Pillowdjango
安裝pillow庫:pip3 install pillow
引入Pillow中最重要的類Image,該類存在於同名的模塊中。能夠經過如下幾種方式實例化:從文件中讀取圖片,處理其餘圖片獲得,或者直接建立一個圖片。
import random def get_validCode_img(request): def get_random_color(): return (random.randint(0,255), random.randint(0, 255), random.randint(0, 255)) # 方式二:pip3 install pillow from PIL import Image img = Image.new("RGB", (270, 40), color=get_random_color()) # 獲得img對象,顏色三要素:紅綠藍 with open("validCode.png", "wb") as f: img.save(f, "png") # 保存動態生成的圖片 with open("validCode.png", "rb") as f: data = f.read() return HttpResponse(data)
運行效果以下:
每次刷新,隨機圖片的顏色會發生隨機變換。可是這種方式是請求進來時,先把數據加載到磁盤上,再在磁盤把數據讀出來返還給瀏覽器,並且磁盤的處理數據時很是慢的,所以應該交到內存中管理。
import random def get_validCode_img(request): def get_random_color(): return (random.randint(0,255), random.randint(0, 255), random.randint(0, 255)) # 方式三:要引入BytesIO from PIL import Image from io import BytesIO img = Image.new("RGB", (270, 40), color=get_random_color()) # 獲得img對象,顏色三要素:紅綠藍 # f爲內存句柄 f= BytesIO() # 會本身處理內存回收 # 保存圖片 img.save(f, "png") data = f.getvalue() return HttpResponse(data)
ImageDraw模塊提供了圖像對象的簡單2D繪製。用戶可使用這個模塊建立新的圖像,註釋或潤飾已存在圖像,爲web應用實時產生各類圖形。
draw.text() 寫文字 參數: xy:座標 text:文本內容 fill:文本顏色 font:文本樣式 draw.line() 畫線 draw.point() 畫點
ImageFont
模塊中,可使用 load()
函數加載一個 bitmap
字體,使用 truetype(fontfile, fontsize)
函數加載一個 OpenType/TrueType
字體(注意,這個函數須要額外安裝_imageingft
模塊)。
在static目錄下建立font子目錄,給用戶存放字體文件。下載字體到該目錄./cnblog/static/font/下。
import random def get_validCode_img(request): # 隨機顏色 def get_random_color(): return (random.randint(0,255), random.randint(0, 255), random.randint(0, 255)) # 方式四: from PIL import Image, ImageDraw,ImageFont from io import BytesIO img = Image.new("RGB", (270, 40), color=get_random_color()) # 獲得img對象,顏色三要素:紅綠藍 # 建立Draw對象 draw = ImageDraw.Draw(img) # 建立Font對象 kumo_font = ImageFont.truetype("static/font/kumo.ttf", size=20) draw.text((0,5), "python", get_random_color(), font=kumo_font) f = BytesIO() # f爲內存句柄 img.save(f, "png") data = f.getvalue() return HttpResponse(data)
顯示效果以下所示:
隨機生成大寫字母、小寫字母、數字。
針對隨機字母須要用到chr()方法,用一個範圍在 range(256)內的(就是0~255)整數做參數,返回一個對應的字符(當前整數對應的ascii字符)。
# 隨機字母: """ >>> chr(65) 'A' >>> chr(90) 'Z' >>> chr(97) 'a' >>> chr(122) 'z' """
另外還須要用到choice() 方法,該方法可返回一個列表,元組或字符串的隨機項。choice()是不能直接訪問的,須要導入 random 模塊,而後經過 random 靜態對象調用該方法。
import random def get_validCode_img(request): # 隨機顏色 def get_random_color(): return (random.randint(0,255), random.randint(0, 255), random.randint(0, 255)) # 方式五:修改成隨機字符串 from PIL import Image, ImageDraw, ImageFont from io import BytesIO img = Image.new("RGB", (270, 40), color=get_random_color()) # 獲得img對象,顏色三要素:紅綠藍 # 建立Draw對象 draw = ImageDraw.Draw(img) # 建立Font對象 kumo_font = ImageFont.truetype("static/font/kumo.ttf", size=28) for i in range(5): random_num = str(random.randint(0, 9)) # 隨機數字 random_low_alpha = chr(random.randint(95, 122)) # 隨機小寫字母 random_upper_alpha = chr(random.randint(65, 90)) # 隨機大寫字母 # 三選一:choice() 方法返回一個列表,元組或字符串的隨機項。 random_char = random.choice([random_num, random_low_alpha, random_upper_alpha]) draw.text((i*50+20, 5), random_char, get_random_color(), font=kumo_font) # 座標錯開間距 f = BytesIO() # f爲內存句柄 img.save(f, "png") data = f.getvalue() return HttpResponse(data)
顯示效果以下所示:
添加圖片噪點和噪線的代碼:
# 給驗證碼圖片添加噪點噪線 width = 270 height = 40 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(50): 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())
能夠任意調配噪線和噪點的數量,儘可能保證機器沒法識別,但人能夠識別。
Draw
類提供了 arc(xy, start, end, options)
函數來繪製弧線,參數解析以下所示:
xy 是個長度爲4的列表,用來表示一個 bounding box(邊界區域)。如[x0, y0, x1, y1],分別表示 弧線最左側距離左邊、弧線最頂點距離上邊、弧線最右側距離左邊、弧線最低點距離上邊的距離。 start 和 end 則是弧的起止角度,單位是 °。其中水平向右的方向爲 0°,豎直向下的方向爲 90°,水平向左的方向爲 180°,豎直向上的方向爲 270°。 options 中可用選項: fill = (R, G, B) :指定線條顏色
驗證碼視圖修改以下:
import random def get_validCode_img(request): # 隨機顏色 def get_random_color(): return (random.randint(0,255), random.randint(0, 255), random.randint(0, 255)) from PIL import Image, ImageDraw, ImageFont from io import BytesIO img = Image.new("RGB", (270, 40), color=get_random_color()) # 獲得img對象,顏色三要素:紅綠藍 # 建立Draw對象 draw = ImageDraw.Draw(img) # 建立Font對象 kumo_font = ImageFont.truetype("static/font/kumo.ttf", size=28) for i in range(5): random_num = str(random.randint(0, 9)) # 隨機數字 random_low_alpha = chr(random.randint(95, 122)) # 隨機小寫字母 random_upper_alpha = chr(random.randint(65, 90)) # 隨機大寫字母 # 三選一:choice() 方法返回一個列表,元組或字符串的隨機項。注意:choice()是不能直接訪問的,須要導入 random 模塊,而後經過 random 靜態對象調用該方法。 random_char = random.choice([random_num, random_low_alpha, random_upper_alpha]) draw.text((i*50+20, 5), random_char, get_random_color(), font=kumo_font) # 座標錯開間距 # 給驗證碼圖片添加噪點噪線 width = 270 height = 40 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(50): 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() # f爲內存句柄 img.save(f, "png") data = f.getvalue() return HttpResponse(data)
顯示效果以下所示:
(1)給驗證碼圖片添加id屬性:id="valid_code_img"
<div class="form-group"> <label for="pwd">驗證碼</label> <div class="row"> <div class="col-md-6"> <input type="text" class="valid_code form-control"> </div> <div class="col-md-6"> {# src還能夠設置請求路徑 #} <img width="270" height="40" id="valid_code_img" src="/get_validCode_img/" alt=""> </div> </div> </div>
(2)建立/static/js/目錄,添加jquery-3.3.1.js,在login.html中引入jquery:
<script src="/static/js/jquery-3.3.1.js"></script>
(3)在頁面控制檯操做驗證碼圖片:
每次在$("#valid_code_img")[0].src後面添加一個「?」都會刷新驗證碼圖片。
<script src="/static/js/jquery-3.3.1.js"></script> <script> // 刷新驗證碼 $("#valid_code_img").click(function () { $(this)[0].src+="?" }) </script>
// 登陸驗證 $(".login-btn").click(function () { $.ajax({ url: "", type: "post", data: { user: $("#user").val(), pwd: $("#pwd").val(), valid_code: $("#valid_code").val(), // 本身組csrf鍵值 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), {# csrf_token的值 #} }, success:function (data) { console.log(data) } }) })
給後端提交用戶名密碼、驗證碼作校驗。
注意:發post請求必定要經過csrf校驗,所以要在form中找一個地方加入:
{% csrf_token %}
可是光加這個是不能經過校驗的,這裏與發form請求不一樣,須要本身組csrf_token鍵值:
能夠在這裏看到鍵名:csrfmiddlewaretoken,利用鍵名組csrf鍵值對。
// 本身組csrf鍵值 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), {# csrf_token的值 #}
from django.http import JsonResponse def login(request): if request.method == "POST": response = {"user": None, "msg": None} user = request.POST.get("user") pwd = request.POST.get("pwd") valid_code = request.POST.get("valid_code") # 從session中取到值,一個瀏覽器存一份,不會發生相互干擾 valid_code_str = request.session.get("valid_code_str") if valid_code.upper() == valid_code_str.upper(): # 添加upper()不區分大小寫 pass else: response['msg'] = "valid code error!" return JsonResponse(response) # 字典放進去直接序列化,ajax拿到的就是 格式,不用反序列化了 return render(request, 'login.html')
(1)在校驗用戶名密碼前,要先校驗驗證碼。
(2)注意這裏作驗證碼校驗,可是驗證碼在另外一個視圖函數中,要取到另外一個函數的驗證碼,不能設置爲全局變量,這樣不一樣人登陸時會互相干擾,校驗沒法保證正常完成。
由於session自己就是一個會話跟蹤,可以保存上一次作的行爲、操做、數據,所以利用它能完成驗證碼驗證。
(3)在get_validcode_img視圖函數中須要添加以下代碼:
request.session["valid_code_str"] = valid_code_str """驗證碼生成過程 1 生成一個隨機字符串 2 設置一個COOKIE,{"sessionid":"剛剛生成的隨機字符串"} 3 django-session表中存儲 session-key session-data 隨機字符串 {"valid_code_str": "隨機驗證碼字符"} """
(4)另外因爲驗證碼校驗是不區分大小寫的,在login中校驗驗證碼時,添加upper()方法:
if valid_code.upper() == valid_code_str.upper(): # 添加upper()不區分大小寫
(5)因爲ajax通常都須要return 一個響應字符串,在這裏引入JsonResponse:
from django.http import JsonResponse
字典放進去直接序列化,ajax拿到的就是對象,兩邊都不須要進行json的序列化與反序列化。
驗證碼驗證成功,django_session表保存瀏覽器對應ssession記錄:
一、引入用戶認證組件auth模塊
from django.contrib import auth
二、在驗證碼驗證經過後,運用authenticate()方法完成用戶認證,即驗證用戶名以及密碼是否正確
user = auth.authenticate(username=user, password=pwd)
三、添加一個用戶,在控制檯執行以下命令
$ python3 manage.py createsuperuser
四、驗證用戶信息無誤後,使用login函數給使用django的session框架給某個已認證的用戶附加上session id等信息
auth.login(request, user) # request.user:當前登陸對象
request.user是全局變量,在任何視圖和模板中能夠直接使用。
五、運用ajax,在頁面顯示報錯信息
(1)修改提交按鈕的input標籤樣式,並在後面加span標籤
<input type="button" class="btn btn-default login-btn" value="提交"><span class="error"></span>
(2)編寫ajax請求回調函數:
// 登陸驗證 $(".login-btn").click(function () { $.ajax({ url: "", type: "post", data: { user: $("#user").val(), pwd: $("#pwd").val(), valid_code: $("#valid_code").val(), // 本身組csrf鍵值 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), {# csrf_token的值 #} }, success:function (data) { console.log(data); if (data.user){ // 若是有值:前端跳轉 location.href = "/index/" } else { // 若是沒值 $(".error").text(data.msg).css({"color": "red", "margin-left": "10px"}) } } }) })
注意:前端跳轉寫法和錯誤信息樣式修改方式。
(3)添加index路由和視圖
path('index/', views.index),
視圖:
def index(request): return render(request, "index.html")
index模板:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>首頁{{ request.user.username }}</h3> </body> </html>
(4)效果圖以下所示:
// 登陸驗證 $(".login-btn").click(function () { $.ajax({ url: "", type: "post", data: { user: $("#user").val(), pwd: $("#pwd").val(), valid_code: $("#valid_code").val(), // 本身組csrf鍵值 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), {# csrf_token的值 #} }, success:function (data) { console.log(data); if (data.user){ // 若是有值:前端跳轉 location.href = "/index/" } else { // 若是沒值 $(".error").text(data.msg).css({"color": "red", "margin-left": "10px"}) setTimeout(function () { $(".error").text(""); // 一秒後清空錯誤提示 }, 1000) } } }) })
這裏主要是用到了javascript中的setTimeout()方法,用來設定一個時間, 時間到了, 就會執行一個指定的 method。
驗證碼功能代碼很是多且邏輯複雜,將這一部分邏輯構建爲一個模塊,視圖中調用這個模塊,實現程序解耦:
(1)建立./blog/utils/目錄,建立文件validCode.py,將驗證碼功能相關代碼拷入該文件中:
import random def get_random_color(): # 隨機顏色 return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) def get_valid_code_imge(request): from PIL import Image, ImageDraw, ImageFont from io import BytesIO img = Image.new("RGB", (270, 40), color=get_random_color()) # 獲得img對象,顏色三要素:紅綠藍 # 建立Draw對象 draw = ImageDraw.Draw(img) # 建立Font對象 kumo_font = ImageFont.truetype("static/font/kumo.ttf", size=28) valid_code_str = "" for i in range(5): random_num = str(random.randint(0, 9)) # 隨機數字 random_low_alpha = chr(random.randint(95, 122)) # 隨機小寫字母 random_upper_alpha = chr(random.randint(65, 90)) # 隨機大寫字母 # 三選一:choice() 方法返回一個列表,元組或字符串的隨機項。 random_char = random.choice([random_num, random_low_alpha, random_upper_alpha]) draw.text((i * 50 + 20, 5), random_char, get_random_color(), font=kumo_font) # 座標錯開間距 # 保存驗證碼字符串 valid_code_str += random_char # 給驗證碼圖片添加噪點噪線 # width = 270 # height = 40 # 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(50): # 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()) print("valid_code_str", valid_code_str) # valid_code_str Ms4v0 # 爲何用request.session: # 由於session自己就是一個會話跟蹤,可以保存上一次作的行爲、操做、數據,所以利用它能完成驗證碼驗證 request.session["valid_code_str"] = valid_code_str f = BytesIO() # f爲內存句柄 img.save(f, "png") data = f.getvalue() return data
(2)在視圖函數中引入該模塊,實現驗證碼功能
def get_validCode_img(request): """ 基於PIL模塊動態生成響應狀態碼圖片 :param request: :return: """ from blog.utils.validCode import get_valid_code_imge data = get_valid_code_imge(request) return HttpResponse(data)
一、一次請求伴隨了屢次請求(伴隨了多個靜態文件的請求)
二、PIL模塊掌握,驗證碼
三、session存儲
四、驗證碼刷新