Django實現(xiàn)驗證碼

Django實現(xiàn)驗證碼

背景知識

1. 驗證碼的作用

  • 防惡意破解密碼:防止,使用程序或機器人惡意去試密碼.為了提高用戶的體驗,用戶輸入錯誤以后,才會要求輸入驗證碼.
  • 防論壇灌水:這個是很常見的。有一種程序叫做頂帖機做入,如果無限制的刷,整個論壇可能到處是拉圾信息同衣,比如竟块,百度貼吧 ,你只要是新用戶或者剛剛關注的貼吧耐齐,要是發(fā)帖浪秘,會馬上出現(xiàn)驗證碼。
  • 有效防止注冊埠况,以防耸携,使用程序或機器人去無限制注冊賬號.
  • 防刷票,網(wǎng)上有很多投票類的網(wǎng)站.

2. 驗證碼的原理

驗證碼于服務器端生成辕翰,發(fā)送給客戶端夺衍,并以圖像格式顯示∠裁客戶端提交所顯示的驗證碼沟沙,客戶端接收并進行比較,若比對失敗則不能實現(xiàn)登錄或注冊壁榕,反之成功后跳轉相應界面尝胆。

驗證碼原理與流程

代碼實現(xiàn)

廢話不多說,先上代碼:
# encoding:utf-8
from PIL import Image, ImageDraw, ImageFont
import random, StringIO
import os
from math import ceil
import base64

current_path = os.path.normpath(os.path.dirname(__file__))


class Captcha(object):
# 定義一個驗證碼類护桦,
def __init__(self, request):
    self.django_request = request
    self.session_key = request.session.session_key
    self.words = []

    # image size (pix)
    self.img_width = 150
    self.img_height = 30

    # default type
    self.type = 'number'

def _get_font_size(self):
    """  將圖片高度的80%作為字體大小
    """
    s1 = int(self.img_height * 0.8)
    s2 = int(self.img_width / len(self.code))
    return int(min((s1, s2)) + max((s1, s2)) * 0.05)

def _get_words(self):
    """ The words list
    """
    # 擴充單詞列表
    if self.words:
        return set(self.words)

    file_path = os.path.join(current_path, 'words.list')
    f = open(file_path, 'r')
    return set([line.replace('\n', '') for line in f.readlines()])

def _set_answer(self, answer):
    """  設置答案
    """
    self.django_request.session[self.session_key] = str(answer)

def _yield_code(self):
    """  生成驗證碼數(shù)字,以及答案
    """
   # 數(shù)字公式驗證碼
    def number():
        m, n = 1, 50
        x = random.randrange(m, n)
        y = random.randrange(m, n)

        r = random.randrange(0, 2)
        if r == 0:
            code = "%s - %s = ?" % (x, y)
            z = x - y
        else:
            code = "%s + %s = ?" % (x, y)
            z = x + y
        self._set_answer(z)
        return code

    fun = eval(self.type.lower())
    return fun()

def display(self):
    """  把生成的驗證碼圖片改成數(shù)據(jù)流返回
    """

    # 字體顏色
    self.font_color = ['black', 'darkblue', 'darkred']

    # 背景顏色含衔,隨機生成
    self.background = (random.randrange(230, 255), random.randrange(230, 255), random.randrange(230, 255))

    # 字體
    self.font_path = os.path.join(current_path, 'timesbi.ttf')
    # self.font_path = os.path.join(current_path,'Menlo.ttc')

    # 生成的驗證碼只做一次驗證,就會清空
    self.django_request.session[self.session_key] = ''

    # 使用 PIL創(chuàng)建畫布
    im = Image.new('RGB', (self.img_width, self.img_height), self.background)
    
    # 生成驗證碼
    self.code = self._yield_code()

    # 設置字體大小
    self.font_size = self._get_font_size()

    # 實例化一個繪圖
    draw = ImageDraw.Draw(im)

    # 在畫布繪圖,寫驗證碼
    if self.type == 'word':
        c = int(8 / len(self.code) * 3) or 3
    elif self.type == 'number':
        c = 4

    for i in range(random.randrange(c - 2, c)):
        line_color = (random.randrange(0, 255), random.randrange(0, 255), random.randrange(0, 255))
        xy = (
            random.randrange(0, int(self.img_width * 0.2)),
            random.randrange(0, self.img_height),
            random.randrange(3 * self.img_width / 4, self.img_width),
            random.randrange(0, self.img_height)
        )
        draw.line(xy, fill=line_color, width=int(self.font_size * 0.1))
        # draw.arc(xy,fill=line_color,width=int(self.font_size*0.1))
    # draw.arc(xy,0,1400,fill=line_color)
    # code part
    j = int(self.font_size * 0.3)
    k = int(self.font_size * 0.5)
    x = random.randrange(j, k)  # starts point
    for i in self.code:
        # 上下抖動量,字數(shù)越多,上下抖動越大
        m = int(len(self.code))
        y = random.randrange(1, 3)
        if i in ('+', '=', '?'):
            # 對計算符號等特殊字符放大處理
            m = ceil(self.font_size * 0.8)
        else:
            # 字體大小變化量,字數(shù)越少,字體大小變化越多
            m = random.randrange(0, int(45 / self.font_size) + int(self.font_size / 5))
        self.font = ImageFont.truetype(self.font_path.replace('\\', '/'), self.font_size + int(ceil(m)))
        draw.text((x, y), i, font=self.font, fill=random.choice(self.font_color))
        x += self.font_size * 0.9
    del x
    del draw
    # 序列化處理
    buf = StringIO.StringIO()
    im.save(buf, 'gif')
    buf.closed
    data = base64.encodestring(buf.getvalue())
    return data

def validate(self, code):
    """
    檢查用戶輸入和服務器上的密碼是否一致
    """
    if not code:
        return False
    _code = self.django_request.session.get(self.session_key) or ''
    self.django_request.session[self.session_key] = ''
    return _code.lower() == str(code).lower()

def check(self, code):
    """
    檢查用戶輸入和服務器上保存的密碼是否一致
    """
    return self.validate(code)

上面使用的庫如下:

    from PIL import Image, ImageDraw, ImageFont
    import random, StringIO
    import os
    from math import ceil
    import base64

說明:

  • PIL 畫圖,生成圖片
  • random 隨機生成數(shù) math用于計算
  • StringIO將圖片格式轉成數(shù)據(jù)流用于網(wǎng)絡傳輸
  • base64,用戶編碼,數(shù)據(jù)傳輸,應前端要求處理跨域API的問題

需要強調的是:
我把用戶的驗證碼的答案保存在用戶的session中,保存在服務器上

self.django_request.session[self.session_key] = str(answer)

每一個用戶訪問都是會實例化一個request.seesion對象,所以,用戶區(qū)分開了.

self.session_key = request.session.session_key

同一用戶在不同地方同時登錄,對應的request.session.session_key不同,所以也區(qū)分了異地同時登錄,出現(xiàn)混亂的情況.

django的view視圖

from common.CaptchaVerify import Captcha

def captchaCode(request):
    ca = Captcha(request)
    ca.type = 'number'
    raw = ca.display()
    response = JsonResponse(raw)
    return response

def login(request):
    _code = request.POST.get('code') or ''
    if not _code:
        data = {'code': 1, 'messeage': 'verify fail, captchacode error'}
        response = ReturnJson(data, status=401).get()
        return response
    ca = Captcha(request)
    if not ca.check(_code):
        data = {'code': 1, 'messsage': 'verify fail, captchacode error'}
        response = ReturnJson(data, status=401).get()
        return response

這里,用戶可以直接調用,那個類,如果需要定制,可以自己在類中修改,符合自己的業(yè)務需求.

ps:
驗證碼繪制規(guī)則
  • 均勻繪畫字符二庵,居中
  • 字符顏色要比較深
  • 要有線條雪花等干擾元素
  • 一切能隨機的都隨機
  • 考慮到用戶的體驗,老是錯誤,開始降低難度(哈哈哈!!)

*下期預告

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末贪染,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子催享,更是在濱河造成了極大的恐慌杭隙,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件因妙,死亡現(xiàn)場離奇詭異痰憎,居然都是意外死亡,警方通過查閱死者的電腦和手機攀涵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門铣耘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人以故,你說我怎么就攤上這事蜗细。” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵炉媒,是天一觀的道長踪区。 經常有香客問我,道長吊骤,這世上最難降的妖魔是什么缎岗? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮白粉,結果婚禮上密强,老公的妹妹穿的比我還像新娘蜗元。我一直安慰自己或渤,他們只是感情好,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布奕扣。 她就那樣靜靜地躺著薪鹦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惯豆。 梳的紋絲不亂的頭發(fā)上池磁,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音楷兽,去河邊找鬼地熄。 笑死,一個胖子當著我的面吹牛芯杀,可吹牛的內容都是我干的端考。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼揭厚,長吁一口氣:“原來是場噩夢啊……” “哼却特!你這毒婦竟也來了?” 一聲冷哼從身側響起筛圆,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤裂明,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后太援,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闽晦,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年提岔,在試婚紗的時候發(fā)現(xiàn)自己被綠了仙蛉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出墨坚,到底是詐尸還是另有隱情,我是刑警寧澤巧还,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站坊秸,受9級特大地震影響麸祷,放射性物質發(fā)生泄漏。R本人自食惡果不足惜褒搔,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一阶牍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧星瘾,春花似錦走孽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至念逞,卻和暖如春困食,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翎承。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工硕盹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叨咖。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓瘩例,卻偏偏與公主長得像,于是被迫代替她去往敵國和親甸各。 傳聞我的和親對象是個殘疾皇子仰剿,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容

  • 經過對django的初步學習,我們已經對后臺的基本流程以及django的運作有了一定的了解痴晦,但是這還不足夠南吮,dja...
    coder_ben閱讀 3,830評論 8 34
  • 22年12月更新:個人網(wǎng)站關停,如果仍舊對舊教程有興趣參考 Github 的markdown內容[https://...
    tangyefei閱讀 35,181評論 22 257
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理誊酌,服務發(fā)現(xiàn)部凑,斷路器,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • Refer to: www.threemeal.com/blog/12/ 中間件 中間件是一個鉤子框架碧浊,它們可以介...
    蘭山小亭閱讀 16,483評論 9 165
  • 這篇文章在博客園有出處的涂邀,是某位大神創(chuàng)作的,然后自己為了研究用戶登錄注冊的問題箱锐,所以把它copy到簡書上比勉,希望可以...
    軒轅小愛閱讀 10,555評論 0 7