譯者 | 布加迪
審校 | 重樓
即使您的密碼被盜,OTP驗(yàn)證系統(tǒng)也可以充當(dāng)安全的關(guān)鍵要素。它讓您無(wú)需記住密碼,充當(dāng)額外的安全層,并降低了網(wǎng)絡(luò)釣魚的風(fēng)險(xiǎn)。
不妨學(xué)習(xí)用Python/ target=_blank class=infotextkey>Python建立一個(gè)OTP驗(yàn)證系統(tǒng),它會(huì)向您的手機(jī)號(hào)碼發(fā)送一個(gè)OTP,有效期只有兩分鐘,如果您連續(xù)三次輸錯(cuò)OTP,賬戶會(huì)被鎖住。
安裝Tkinter、Twilio和Random模塊
Tkinter允許您創(chuàng)建桌面應(yīng)用程序。它提供了各種小組件,比如按鈕、標(biāo)簽和文本框,使開(kāi)發(fā)應(yīng)用程序變得更容易。
Twilio模塊幫助您把短信、彩信和電話呼叫等通信功能與驗(yàn)證徑直整合到應(yīng)用程序中。它有一個(gè)基于云的基礎(chǔ)設(shè)施,以及令人驚嘆的功能,比如號(hào)碼配置、消息模板和呼叫記錄。
要安裝Twilio模塊和Tkinter模塊,在終端執(zhí)行如下命令:
pip install twilio tk
Random模塊是內(nèi)置的Python模塊,用于生成偽隨機(jī)數(shù)。有了該模塊,您可以生成隨機(jī)數(shù)、從列表中選擇隨機(jī)元素、打亂列表內(nèi)容等。您可以用它來(lái)構(gòu)建擲骰子模擬、列表打亂器或隨機(jī)密碼生成器。
生成Twilio API并獲取電話號(hào)碼
要使用Twilio并向您的手機(jī)發(fā)送OTP請(qǐng)求,您需要身份驗(yàn)證憑據(jù)以及Twilio電話號(hào)碼。為此:
1. 注冊(cè)一個(gè)Twilio賬戶,訪問(wèn)Twilio控制臺(tái)。
2. 向下滾動(dòng)并點(diǎn)擊“獲取電話號(hào)碼”按鈕。復(fù)制已生成的電話號(hào)碼。
3. 向下滾動(dòng)到“賬戶信息”部分。復(fù)制“賬戶SID”和“身份驗(yàn)證令牌”。
構(gòu)建應(yīng)用程序的結(jié)構(gòu)
事先聲明一下,您可以在這個(gè)Github代碼倉(cāng)庫(kù)中找到使用Python構(gòu)建OTP驗(yàn)證系統(tǒng)的完整源代碼。
導(dǎo)入必要的模塊,并設(shè)置身份驗(yàn)證憑據(jù)。初始化Twilio客戶軟件以驗(yàn)證身份,并作為API調(diào)用的入口點(diǎn)。將到期失效時(shí)間設(shè)為兩分鐘。
定義一個(gè)類:OTPVerification,并初始化構(gòu)造函數(shù)以設(shè)置變量的默認(rèn)值,同時(shí)初始化根窗口,并設(shè)置應(yīng)用程序的標(biāo)題和維度。
import tkinter as tk
from tkinter import messagebox
from twilio.rest import Client
import random
import threading
import time
account_sid = "YOUR_ACCOUNT_SID"
auth_token = "YOUR_AUTH_TOKEN"
client = Client(account_sid, auth_token)
expiration_time = 120
class OTPVerification:
def __init__(self, master):
self.master = master
self.master.title('OTP Verification')
self.master.geometry("600x275")
self.otp = None
self.timer_thread = None
self.resend_timer = None
self.wrong_attempts = 0
self.locked = False
self.stop_timer = False
定義三個(gè)標(biāo)簽來(lái)請(qǐng)求手機(jī)號(hào)碼和OTP,并在程序發(fā)送OTP后顯示計(jì)時(shí)器。設(shè)置父元素、它應(yīng)該顯示的文本以及該有的字體樣式。同樣,創(chuàng)建兩個(gè)輸入小組件以獲取用戶輸入。設(shè)置父元素、寬度和字體樣式。
創(chuàng)建三個(gè)按鈕來(lái)發(fā)送OTP、重新發(fā)送OTP和驗(yàn)證OTP。設(shè)置父元素、它應(yīng)該顯示的文本、點(diǎn)擊時(shí)執(zhí)行的命令及其字體樣式。使用pack方法組織這些元素。
self.label1 = tk.Label(self.master,
text='Enter your mobile number:',
fnotallow=('Arial', 14))
self.label1.pack()
self.mobile_number_entry = tk.Entry(self.master,
width=20,
fnotallow=('Arial', 14))
self.mobile_number_entry.pack()
self.send_otp_button = tk.Button(self.master,
text='Send OTP',
command=self.send_otp,
fnotallow=('Arial', 14))
self.send_otp_button.pack()
self.timer_label = tk.Label(self.master,
text='',
fnotallow=('Arial', 12, 'bold'))
self.timer_label.pack()
self.resend_otp_button = tk.Button(self.master,
text='Resend OTP',
state=tk.DISABLED,
command=self.resend_otp,
fnotallow=('Arial', 14))
self.resend_otp_button.pack()
self.label2 = tk.Label(self.master,
text='Enter OTP sent to your mobile:',
fnotallow=('Arial', 14))
self.label2.pack()
self.otp_entry = tk.Entry(self.master,
width=20,
fnotallow=('Arial', 14))
self.otp_entry.pack()
self.verify_otp_button = tk.Button(self.master,
text='Verify OTP',
command=self.verify_otp,
fnotallow=('Arial', 14))
self.verify_otp_button.pack()
構(gòu)建應(yīng)用程序的功能
定義一個(gè)方法start_timer(),它在單獨(dú)的線程中運(yùn)行timer_countdown。
def start_timer(self):
self.timer_thread = threading.Thread(target=self.timer_countdown)
self.timer_thread.start()
定義一個(gè)方法timer_countdown()。記錄開(kāi)始時(shí)間,并運(yùn)行一個(gè)無(wú)限循環(huán),該循環(huán)獲取當(dāng)前時(shí)間并計(jì)算已流逝的時(shí)間和剩余時(shí)間。如果stop_timer為true,終止循環(huán)。如果剩余時(shí)間小于或等于0,顯示錯(cuò)誤消息框,表明OTP已過(guò)期。
激活重新發(fā)送OTP按鈕,將OTP設(shè)置為none,并終止。否則,計(jì)算剩余的分鐘和秒,將其顯示在計(jì)時(shí)器標(biāo)簽上,并休眠一秒鐘。
def timer_countdown(self):
start_time = time.time()
while True:
current_time = time.time()
elapsed_time = current_time - start_time
remAIning_time = expiration_time - elapsed_time
if self.stop_timer:
break
if remaining_time <= 0:
messagebox.showerror('Error', 'OTP has expired.')
self.resend_otp_button.config(state=tk.NORMAL)
self.otp = None
break
minutes = int(remaining_time // 60)
seconds = int(remaining_time % 60)
timer_label = f'Time Remaining: {minutes:02d}:{seconds:02d}'
self.timer_label.config(text=timer_label)
time.sleep(1)
定義一個(gè)方法send_otp()。如果locked為true,顯示相應(yīng)的消息。否則提取并驗(yàn)證電話號(hào)碼,生成一個(gè)隨機(jī)的OTP。提供之前獲取的手機(jī)號(hào)碼,使用客戶軟件將OTP發(fā)送到您的電話號(hào)碼。顯示消息框,啟動(dòng)計(jì)時(shí)器,禁用按鈕,并完全清除輸入內(nèi)容。
def send_otp(self):
if self.locked:
messagebox.showinfo('Account Locked', 'Your account is locked. Try again later.')
return
mobile_number = self.mobile_number_entry.get()
if not mobile_number:
messagebox.showerror('Error', 'Please enter your mobile number.')
return
self.otp = random.randint(1000, 9999)
message = client.messages.create(
body=f'Your OTP is {self.otp}.',
from_='TWILIO_MOBILE_NUMBER',
to=mobile_number
)
messagebox.showinfo('OTP Sent', f'OTP has been sent to {mobile_number}.')
self.start_timer()
self.send_otp_button.config(state=tk.DISABLED)
self.resend_otp_button.config(state=tk.DISABLED)
self.otp_entry.delete(0, tk.END)
def send_otp(self):
if self.locked:
messagebox.showinfo('Account Locked', 'Your account is locked. Try again later.')
return
mobile_number = self.mobile_number_entry.get()
if not mobile_number:
messagebox.showerror('Error', 'Please enter your mobile number.')
return
self.otp = random.randint(1000, 9999)
message = client.messages.create(
body=f'Your OTP is {self.otp}.',
from_='TWILIO_MOBILE_NUMBER',
to=mobile_number
)
messagebox.showinfo('OTP Sent', f'OTP has been sent to {mobile_number}.')
self.start_timer()
self.send_otp_button.config(state=tk.DISABLED)
self.resend_otp_button.config(state=tk.DISABLED)
self.otp_entry.delete(0, tk.END)
定義一個(gè)方法resend_otp()。如果鎖住,顯示相應(yīng)的消息。否則獲取并驗(yàn)證電話號(hào)碼,重新生成隨機(jī)的OTP,重新發(fā)送OTP,顯示消息框,啟動(dòng)計(jì)時(shí)器,并禁用重新發(fā)送OTP按鈕。
def resend_otp(self):
if self.locked:
messagebox.showinfo('Account Locked', 'Your account is locked. Try
again later.')
return
mobile_number = self.mobile_number_entry.get()
if not mobile_number:
messagebox.showerror('Error', 'Please enter your mobile number.')
return
self.otp = random.randint(1000, 9999)
message = client.messages.create(
body=f'Your OTP is {self.otp}.',
from_='TWILIO_MOBILE_NUMBER',
to=mobile_number
)
messagebox.showinfo('OTP Sent', f'New OTP has been sent to {mobile_number}.')
self.start_timer()
self.resend_otp_button.config(state=tk.DISABLED)
定義一個(gè)方法verify_otp()。獲取OTP,并檢查用戶是否沒(méi)有輸入任何內(nèi)容。如果存儲(chǔ)的OTP為None,要求用戶先生成OTP。如果用戶輸入的OTP與存儲(chǔ)的OTP匹配,顯示OTP驗(yàn)證成功,停止計(jì)時(shí)器,并退出程序。否則檢查錯(cuò)誤的輸入嘗試。如果輸錯(cuò)次數(shù)超過(guò)3次,鎖住賬戶。
def verify_otp(self):
user_otp = self.otp_entry.get()
if not user_otp:
messagebox.showerror('Error', 'Please enter OTP.')
return
if self.otp is None:
messagebox.showerror('Error', 'Please generate OTP first.')
return
if int(user_otp) == self.otp:
messagebox.showinfo('Success', 'OTP verified successfully.')
self.stop_timer = True
exit()
else:
self.wrong_attempts += 1
if self.wrong_attempts == 3:
self.lock_account()
else:
messagebox.showerror('Error', 'OTP does not match.')
定義一個(gè)方法lock_account()。設(shè)置鎖住狀態(tài)為true,顯示標(biāo)簽為“賬戶已鎖住”。禁用所有標(biāo)簽、條目和按鈕。停止現(xiàn)有的計(jì)時(shí)器,啟動(dòng)新的計(jì)時(shí)器(10分鐘)。
def lock_account(self):
self.locked = True
self.label1.config(text='Account Locked')
self.mobile_number_entry.config(state=tk.DISABLED)
self.send_otp_button.config(state=tk.DISABLED)
self.timer_label.config(text='')
self.resend_otp_button.config(state=tk.DISABLED)
self.label2.config(text='')
self.otp_entry.config(state=tk.DISABLED)
self.verify_otp_button.config(state=tk.DISABLED)
self.stop_timer = True
countdown_time = 10 * 60
self.start_countdown(countdown_time)
定義一個(gè)方法start_countdown()。如果剩余時(shí)間小于等于0,重置賬戶。否則顯示程序已鎖住賬戶,并在剩余時(shí)間內(nèi)使用回調(diào)再試一次。
def start_countdown(self, remaining_time):
if remaining_time <= 0:
self.reset_account()
return
minutes = int(remaining_time // 60)
seconds = int(remaining_time % 60)
timer_label = f'Account Locked. Try again in:
{minutes:02d}:{seconds:02d}'
self.timer_label.config(text=timer_label)
self.master.after(1000, self.start_countdown, remaining_time - 1)
定義一個(gè)函數(shù)reset_account()。像前面一樣重置所有小組件和變量的狀態(tài)。
def reset_account(self):
self.locked = False
self.wrong_attempts = 0
self.label1.config(text='Enter your mobile number:')
self.mobile_number_entry.config(state=tk.NORMAL)
self.send_otp_button.config(state=tk.NORMAL)
self.timer_label.config(text='')
self.resend_otp_button.config(state=tk.DISABLED)
self.label2.config(text='Enter OTP sent to your mobile:')
self.otp_entry.config(state=tk.NORMAL)
self.verify_otp_button.config(state=tk.NORMAL)
self.stop_timer = False
創(chuàng)建根窗口和類的實(shí)例,并運(yùn)行Tkinter應(yīng)用程序。
if __name__ == '__main__':
root = tk.Tk()
otp_verification = OTPVerification(root)
root.mainloop()
使用OTP驗(yàn)證的輸出示例
在運(yùn)行OTP驗(yàn)證程序時(shí),您會(huì)看到一個(gè)窗口,要求輸入手機(jī)號(hào)碼。輸入手機(jī)號(hào)碼以及所在國(guó)家代號(hào),然后點(diǎn)擊“發(fā)送OTP”按鈕。您會(huì)收到一條消息,表明程序已成功發(fā)送OTP,按鈕會(huì)停用兩分鐘。檢查手機(jī)是否收到了OTP,并在過(guò)期前輸入它。
在計(jì)時(shí)器過(guò)期前輸入正確的OTP后,您將收到一條消息,表明程序已成功驗(yàn)證了OTP,退出程序。如果您沒(méi)有及時(shí)輸入,會(huì)收到消息框,表明OTP已過(guò)期。可以點(diǎn)擊“重新發(fā)送OTP”按鈕以生成新的OTP,并發(fā)送到您的手機(jī)。
如果您輸錯(cuò)了OTP,程序?qū)@示一個(gè)消息框,表明“OTP不匹配”。
如果OTP輸錯(cuò)三次,所有字段將被禁用,賬戶將被鎖住十分鐘。
結(jié)合使用Twilio與Python
使用Twilio,您可以為各種事件構(gòu)建短信通知系統(tǒng)。您可以將其與物聯(lián)網(wǎng)設(shè)備一起使用,當(dāng)設(shè)備的數(shù)值高于或低于某個(gè)閾值或者檢測(cè)到入侵者時(shí)發(fā)送短信。您還可以構(gòu)建具有雙因素身份驗(yàn)證的安全登錄系統(tǒng),構(gòu)建WhatsApp聊天機(jī)器人和約會(huì)提醒系統(tǒng)。
除此之外,您還可以用它進(jìn)行電話號(hào)碼驗(yàn)證、營(yíng)銷活動(dòng)、發(fā)送調(diào)查表和收集反饋。在構(gòu)建任何應(yīng)用程序時(shí),始終留意Twilio API的定價(jià),以免遭遇意外成本。
原文標(biāo)題:How to Build an OTP Verification System Using Python,作者:Sai Ashish Konchada