基于Python實現(xiàn)原生的登錄驗證碼詳情
1、概述
在前面的文章中,我有分享了vue+drf+第三方滑動驗證碼接入的實現(xiàn) (文中也留了分享圖片驗證碼功能的實現(xiàn)),即本文將要分享的是基于 python
實現(xiàn)原生的登錄驗證碼
通常的驗證碼,人眼看上去更像是一張小圖片
在 html
語法中,嵌入一張圖片一般用 img
標簽實現(xiàn),而 img
標簽對應的 src
一般有以下幾種寫法
- 圖片的本地路徑
- 圖片的
url
- 圖片的二進制數(shù)據(jù)(
base64
編碼)
其中前兩種方法都需要外部具有實際存在的圖片,第三種方法則是將圖片進行編碼后填充到 img
標簽的 src
下
2、驗證碼實現(xiàn)的演進過程
2.1 路由及頁面
為了實現(xiàn)驗證碼的功能,需要開設一個 url
單獨處理驗證碼功能,修改全局路由
urlpatterns = [ ...... url(r'^login/', views.login, name='login'), # 圖片驗證碼 url(r'^get_code/', views.get_code, name='gc'), ... ]
然后修改前端登錄頁面 login.html
的驗證碼部分
... <div class="form-group"> <label for="">驗證碼</label> <div class="row"> <div class="col-md-6"> <input type="text" name="code" id="id_code" class="form-control"> </div> <div class="col-md-6"> <img src="/get_code/" alt="" width="430" height="35" id="id_img"> </div> </div> </div> <input type="button" class="btn btn-success" value="登陸" id="id_commit"> ...
2.2 視圖函數(shù)中驗證碼的推導
2.2.1 圖片發(fā)送到前端
前端界面實現(xiàn)了一個簡單的包含驗證碼的登錄框,首先定義一個視圖函數(shù)將后端的測試圖片以二進制的形式發(fā)送到前端進行測試
def get_code(request): # 直接獲取后端現(xiàn)成的圖片二進制數(shù)據(jù)發(fā)送給前端 with open(r'static/img/test.jpg','rb') as f: data = f.read() return HttpResponse(data)
2.2.2 引入動態(tài)圖片
為了操作圖片,主要利用的是 pillow
模塊
pip3 install pillow
主要用到了 Image
、 ImageDraw
、 ImageFont
Image
:生成圖片ImageDraw
:在圖片上定義內(nèi)容ImageFont
:控制字體樣式
因此,利用 pillow
模塊動態(tài)產(chǎn)生圖片的方法為
import random from PIL import Image, ImageDraw, ImageFont def get_random(): return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) def get_code(request): # 利用pillow模塊動態(tài)產(chǎn)生圖片 # img_obj = Image.new('RGB',(430,35),'green') # RGB組合、圖片尺寸、顏色 img_obj = Image.new('RGB',(430,35),get_random()) # 通過色值指定顏色 # 先將圖片對象保存起來 with open('xxx.png','wb') as f: img_obj.save(f,'png') # 再將圖片對象讀取出來 with open('xxx.png','rb') as f: data = f.read() return HttpResponse(data)
2.2.3 內(nèi)存管理模塊圖片
上面將圖片對象保存在讀取有些麻煩,文件存儲繁瑣 IO
操作效率低 ,可以借助于內(nèi)存管理器模塊實現(xiàn)
其中又分為 BytesIO
和 StringIO
兩種
BytesIO
:臨時存儲數(shù)據(jù),返回的時候數(shù)據(jù)是二進制StringIO
:臨時存儲數(shù)據(jù),返回的時候數(shù)據(jù)是字符串
內(nèi)存管理對象就相當于上面的文件句柄
import random from PIL import Image, ImageDraw, ImageFont def get_random(): return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) def get_code(request): img_obj = Image.new('RGB', (430, 35), get_random()) io_obj = BytesIO() # 生成一個內(nèi)存管理器對象 你可以看成是文件句柄 img_obj.save(io_obj,'png') return HttpResponse(io_obj.getvalue()) # 從內(nèi)存管理器中讀取二進制的圖片數(shù)據(jù)返回給前端
這樣一來,圖片的生成以及返回就比較友好了
2.2.4 完整圖片驗證碼
上面解決了圖片如何傳遞到前端頁面的問題,剩下的就是如何生成對應的隨機驗證碼了
例如隨機驗證碼為五位數(shù)的隨機驗證碼,包含數(shù)字、小寫字母、大寫字母
import random from PIL import Image, ImageDraw, ImageFont def get_random(): return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) def get_code(request): # 寫圖片驗證碼 img_obj = Image.new('RGB', (430, 35), get_random()) img_draw = ImageDraw.Draw(img_obj) # 產(chǎn)生一個畫筆對象 # 引入本地的字體文件,指定字體樣式及字體大小 img_font = ImageFont.truetype('static/font/222.ttf', 30) # 隨機驗證碼 五位數(shù)的隨機驗證碼 數(shù)字 小寫字母 大寫字母 code = '' for i in range(5): random_upper = chr(random.randint(65, 90)) # 隨機大寫字母 random_lower = chr(random.randint(97, 122)) # 隨機小寫字母 random_int = str(random.randint(0, 9)) # 隨機數(shù)字 # 每次從上面三個里面隨機選擇一個 tmp = random.choice([random_lower, random_upper, random_int]) # 將產(chǎn)生的隨機字符串寫入到圖片上,需要調整每個字體所在的坐標變換 img_draw.text((i * 60 + 60, -2), tmp, get_random(), img_font) # 拼接隨機字符串 code += tmp print(code) # 隨機驗證碼在登陸的視圖函數(shù)里面需要要比對,所以要找地方存起來并且其他視圖函數(shù)也能拿到 request.session['code'] = code io_obj = BytesIO() img_obj.save(io_obj, 'png') return HttpResponse(io_obj.getvalue())
這里有一點,在寫入隨機文字的時候一個個寫而不是生成好了之后再寫,是因為一個個寫能夠控制每個字體的間隙。而生成好之后再寫的話,間隙就沒法控制了
2.3 登錄驗證中使用驗證碼
上面將每次生成的驗證碼存儲到了 session
中,這樣在前端傳過來的驗證碼,登錄校驗時就可以進行比對了
...... def login(request): if request.method == 'POST': back_dic = {'code': 1000, 'msg': ''} username = request.POST.get('username') password = request.POST.get('password') code = request.POST.get('code') # 1 先校驗驗證碼是否正確 自己決定是否忽略大小寫 統(tǒng)一轉大寫或小寫再比較 if request.session.get('code').upper() == code.upper(): # 校驗用戶名和密碼是否正確 user_obj = auth.authenticate(request,username=username,password=password) if user_obj: # 保存用戶狀態(tài) auth.login(request,user_obj) back_dic['url'] = '/home/' else: back_dic['code'] = 2000 back_dic['msg'] = '用戶名或密碼錯誤' else: back_dic['code'] = 3000 back_dic['msg'] = '驗證碼錯誤' return JsonResponse(back_dic) return render(request, 'login.html')
2.4 前端頁面點擊自動刷新
最后,還留下一點小問題,前端在輸入驗證碼錯誤后不會自動刷新,如果點擊驗證碼也不會進行刷新,只能通過刷新登錄頁面才能刷新驗證碼,因此需要想辦法讓用戶在點擊驗證碼時自動刷新(單獨觸發(fā)驗證碼的視圖函數(shù))
每次在點擊時,修改對應 src
的值即可,可以通過一小段 js
實現(xiàn)
... <div class="form-group"> <label for="">驗證碼</label> <div class="row"> <div class="col-md-6"> <input type="text" name="code" id="id_code" class="form-control"> </div> <div class="col-md-6"> <img src="/get_code/" alt="" width="430" height="35" id="id_img"> </div> </div> </div> <input type="button" class="btn btn-success" value="登陸" id="id_commit"> ... <script> $("#id_img").click(function () { // 獲取標簽之前的src let oldVal = $(this).attr('src'); $(this).attr('src',oldVal += '?') }) </script>
3、效果展示
最終前端的驗證碼效果如圖
4、小結
本文基于 python
以及相關的庫原生實現(xiàn)了登錄驗證碼邏輯~
其實寫本文也是因為之前有過想法但是一段時間就忘了,最近通過某銀行手機銀行 app
重置登錄密碼的時候,發(fā)現(xiàn)其驗證碼顯示效果和本文效果竟然神似~,于是撿起來寫了此文
其手機 app
驗證碼效果如下
到此這篇關于基于Python實現(xiàn)原生的登錄驗證碼詳情的文章就介紹到這了,更多相關基于Python實現(xiàn)原生的登錄驗證碼內(nèi)容請搜索本站以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持本站!
版權聲明:本站文章來源標注為YINGSOO的內(nèi)容版權均為本站所有,歡迎引用、轉載,請保持原文完整并注明來源及原文鏈接。禁止復制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務器上建立鏡像,否則將依法追究法律責任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學習參考,不代表本站立場,如有內(nèi)容涉嫌侵權,請聯(lián)系alex-e#qq.com處理。