Flask集成pyotp生成動態口令

????????Python中的pyotp模塊是一個用于生成和驗證一次性密碼(OTP)的庫,支持基于時間(TOTP)和計數器(HOTP)的兩種主流算法。它遵循RFC 4226(HOTP)和RFC 6238(TOTP)標準,兼容Google Authenticator等主流認證工具,廣泛應用于需要增強系統安全性的業務場景中。

后端 python 代碼

# main.py
import pyotp
import base64
import qrcode
import logging
from io import BytesIO
from flask import Flask, request, jsonify, render_templateapp = Flask(__name__)app.config['JSON_AS_ASCII'] = False# 禁止控制臺輸出請求信息
logging.getLogger('werkzeug').disabled = True
# 設置總的日志輸出級別
app.logger.setLevel(logging.ERROR)# 404錯誤處理
@app.errorhandler(404)
def page_not_found(e):# 直接返回字符串return "您請求的資源不存在!", 404# 500錯誤處理
@app.errorhandler(500)
def internal_error(e):return "服務器內部錯誤!", 500# 捕獲所有未處理異常
@app.errorhandler(Exception)
def handle_exception(e):return "發生錯誤,請稍后再試!", 500@app.route('/')
def process_request():return render_template('index.html')@app.route('/generate_secret', methods=['POST'])
def generate_secret():secret = pyotp.random_base32()response = {'status': 0, 'data': secret}return jsonify(response)@app.route('/generate_otp', methods=['POST'])
def generate_otp():response = {'status': 1, 'data': None, 'msg': None}try:data = request.jsonsecret = data['secret']length = int(data['length'])interval = int(data['interval'])if not verify_base32_key(secret):response['msg'] = '無效的密鑰'return jsonify(response)if length>10 or length<4:response['msg'] = '口令的有效長度為 4 ~ 10 位'return jsonify(response)if interval>60 or interval<30:response['msg'] = '口令有效期范圍為 30 ~ 60 秒'return jsonify(response)otp_code = get_otp_code(secret, length, interval)# 生成二維碼img = qrcode.make(otp_code)buffered = BytesIO()img.save(buffered, format="PNG")img_str = base64.b64encode(buffered.getvalue()).decode()response['status'] = 0response['data'] = {'otp_code': otp_code,'qrcode': f"data:image/png;base64,{img_str}"}return jsonify(response)except Exception as e:response['msg'] = f'動態口令生成異常: {str(e)}'return jsonify(response)@app.route('/verify_otp', methods=['POST'])
def verify_otp():data = request.jsonsecret = data['secret']otp_code = data['otp_code']interval = int(data['interval'])response = {'status': 0, 'data': verify_otp_code(secret, otp_code, interval)}return jsonify(response)'''
生成動態口令
secret_key 32位密鑰字符串
digits 動態口令長度
interval 口令有效期
'''
def get_otp_code(secret_key, digits=6, interval=30):totp = pyotp.TOTP(secret_key, digits=digits, interval=interval)otp_code = totp.now()return otp_code'''
校驗動態口令
secret_key 32位密鑰字符串
totp_code 動態口令
interval 口令有效期
'''
def verify_otp_code(secret_key, totp_code, interval=30):totp = pyotp.TOTP(secret_key, digits=len(totp_code), interval=interval)is_valid = totp.verify(totp_code)return is_valid# 驗證32位密鑰, 標準的Base32字母表為A-Z和2-7,不含小寫字母或特殊符號
def verify_base32_key(key):allowed = set('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')if len(key) != 32 or not all(c in allowed for c in key):return Falsetry:base64.b32decode(key, casefold=False)return Trueexcept:return Falseif __name__ == '__main__':app.run(host='0.0.0.0', port=8181)

前端 html 代碼

在項目根目錄下新建一個templates模板目錄,然后在里面創建一個html文件,名稱為 index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0"><title>動態口令生成器</title><style>:root {--primary-color: #2196F3;--secondary-color: #64B5F6;--background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);}* {box-sizing: border-box;margin: 0;padding: 0;font-family: 'Segoe UI', system-ui;}body {min-height: 100vh;display: flex;justify-content: center;align-items: center;background: var(--background);padding: 20px;}.container {background: rgba(255, 255, 255, 0.95);padding: 2rem;border-radius: 15px;box-shadow: 0 10px 30px rgba(0,0,0,0.1);width: 100%;max-width: 600px;transition: transform 0.3s ease;}.form-group {margin-bottom: 1.5rem;}label {display: block;margin-bottom: 0.5rem;color: #2c3e50;font-weight: 500;}input {width: 100%;padding: 12px;border: 2px solid #e0e0e0;border-radius: 8px;font-size: 16px;transition: border-color 0.3s ease;}input:focus {outline: none;border-color: var(--primary-color);box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);}button {background: var(--primary-color);color: white;border: none;padding: 12px 24px;border-radius: 8px;font-size: 16px;cursor: pointer;transition: all 0.3s ease;width: 100%;}button:hover {background: var(--secondary-color);transform: translateY(-2px);box-shadow: 0 5px 15px rgba(33, 150, 243, 0.3);}#result {margin-top: 0rem;text-align: center;}#qrcode {display: none;margin: 0px auto;justify-content: center;align-items: center;width: 100%;}#timer {font-size: 16px;color: #e74c3c;margin-top: 1rem;}#error {color: #e74c3c;}#info {color: #009688;margin-top: 1rem;}.tab-nav {list-style: none;display: flex;margin-bottom: 2rem;border-bottom: 2px solid #eee;}.tab-nav li {padding: 12px 24px;cursor: pointer;color: #666;transition: all 0.3s ease;border-bottom: 2px solid transparent;}.tab-nav li.active {color: var(--primary-color);border-bottom-color: var(--primary-color);}.tab-content {display: none;}.tab-content.active {display: block;}.result-box {display: none;background: #f8f9fa;border-radius: 8px;padding: 1rem;text-align: center;}pre {white-space: pre-wrap;word-wrap: break-word;background: #fff;border-radius: 6px;}.copy-btn {margin-top: 2rem;background: #2F4056 !important;width: auto !important;display: inline-block !important;}@media (max-width: 480px) {.container {padding: 1.5rem;}input, button {font-size: 14px;}}</style>
</head>
<body><div class="container"><ul class="tab-nav"><li class="active" data-tab="create-tab">生成密鑰</li><li data-tab="generate-tab">生成口令</li><li data-tab="verify-tab">口令驗證</li></ul><!-- 密鑰生成界面 --><div id="create-tab" class="tab-content active"><p style="color: #009688; line-height: 30px; font-size: 14px;">聲明:本系統不會存儲任何密鑰,請妥善保管,切勿外泄!如若丟失,請重新生成,并同步更新客戶端和服務端的密鑰信息。</p><button style="margin-top: 2rem; margin-bottom: 1.5rem;" onclick="generateSecret()">生成密鑰</button><div class="result-box"><pre id="new-secret"></pre><button class="copy-btn" onclick="copySecrett()">復制密鑰</button><p id="info"></p></div></div><!-- 動態口令生成界面 --><div id="generate-tab" class="tab-content"><div class="form-group"><label for="secret">密鑰:</label><input type="text" id="secret" placeholder="請輸入32位密鑰" required></div><div class="form-group"><label for="length">動態口令長度:</label><input type="number" id="length" min="4" max="10" value="6" placeholder="請輸入動態口令長度,有效范圍 4 ~ 10 位" required></div><div class="form-group"><label for="length">口令有效期(秒):</label><input type="number" id="interval" min="30" max="60" value="60" placeholder="請輸入動態口令有效期,時間范圍 30 ~ 60 秒" required></div><button onclick="generateOTP()">生成動態口令</button><div id="result"><div id="qrcode"><img id="qrImage" src="" alt="二維碼"></div><p id="otp_code"></p><p id="timer"></p><p id="error"></p></div></div><!-- 口令驗證界面 --><div id="verify-tab" class="tab-content"><p style="color: #009688; line-height: 30px; font-size: 14px; margin-bottom: 1.5rem;">提示:驗證時,必須同時設置密鑰、口令和有效期參數,并確保與口令生成時指定的參數一致!</p><div class="form-group"><label for="secret">密鑰:</label><input type="text" id="verify-secret" placeholder="請輸入32位密鑰" required></div><div class="form-group"><label for="secret">動態口令:</label><input type="text" id="verify-otp_code" placeholder="請輸入動態口令" required></div><div class="form-group"><label for="secret">口令有效期(秒):</label><input type="number" id="verify-interval" min="30" max="60" value="60" placeholder="請輸入動態口令有效期,時間范圍 30 ~ 60 秒" required></div><button style="margin-top: 2rem; margin-bottom: 1.5rem;" onclick="verifyOTP()">驗證</button><div id="verify-info" style="text-align: center;"></div></div></div><script>let countdown;document.querySelectorAll('.tab-nav li').forEach(tab => {tab.addEventListener('click', function() {// 移除所有激活狀態document.querySelectorAll('.tab-nav li, .tab-content').forEach(el => {el.classList.remove('active');});// 設置當前激活狀態this.classList.add('active');document.getElementById(this.dataset.tab).classList.add('active');});});// 生成密鑰async function generateSecret() {document.getElementsByClassName('result-box')[0].style.display = 'none';document.getElementById('info').innerHTML = '';try {const response = await fetch('/generate_secret', {method: 'POST',headers: {'Content-Type': 'application/json',}});const resp = await response.json();const data = resp.data;document.getElementsByClassName('result-box')[0].style.display = 'block';document.getElementById('new-secret').textContent = data;} catch (error) {console.error('Error:', error);}}// 復制密鑰function copySecrett() {const secret = document.getElementById('new-secret').textContent;navigator.clipboard.writeText(secret);document.getElementById('info').innerHTML = '復制成功';}// 生成動態口令async function generateOTP() {document.getElementById('error').innerHTML = '';const secret = document.getElementById('secret').value;const length = document.getElementById('length').value;const interval = document.getElementById('interval').value;try {const response = await fetch('/generate_otp', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({ secret, length, interval })});const resp = await response.json();const data = resp.data;if (resp.status == 1) {clearInterval(countdown);document.getElementById('timer').innerHTML = '';emptyInfo()document.getElementById('error').innerHTML = `${resp.msg}`;return;}document.getElementById('otp_code').innerHTML = `動態口令:<strong>${data.otp_code}</strong>`;document.getElementById('qrImage').src = `${data.qrcode}`;document.getElementById('qrcode').style.display = 'flex';// 啟動倒計時startCountdown(interval);} catch (error) {console.error('Error:', error);}}// 驗證動態口令async function verifyOTP() {document.getElementById('verify-info').innerHTML = '';const secret = document.getElementById('verify-secret').value;const otp_code = document.getElementById('verify-otp_code').value;const interval = document.getElementById('verify-interval').value;try {const response = await fetch('/verify_otp', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({ secret, otp_code, interval })});const resp = await response.json();if (resp.data) {document.getElementById('verify-info').innerHTML = '<p style="color: #009688;">口令正確,驗證成功</p>';} else {document.getElementById('verify-info').innerHTML = '<p style="color: #e74c3c;">口令錯誤或已失效,驗證失敗</p>';}} catch (error) {console.error('Error:', error);}}function startCountdown(seconds) {let remaining = seconds;const timerElement = document.getElementById('timer');clearInterval(countdown);countdown = setInterval(() => {remaining--;timerElement.textContent = `有效期:${remaining} 秒`;if (remaining <= 0) {clearInterval(countdown);timerElement.textContent = '口令已過期';}}, 1000);}function emptyInfo() {document.getElementById('qrcode').style.display = 'none';document.getElementById('otp_code').innerHTML = '';document.getElementById('qrImage').src = '';}</script>
</body>
</html>

界面截圖

🏷? 如有疑問,可以關注 我的知識庫,直接提問即可。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/907324.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/907324.shtml
英文地址,請注明出處:http://en.pswp.cn/news/907324.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

觸控精靈 ADB運行模式填寫電腦端IP教程

?ADB模式&#xff0c;如果你手機已經root則可以直接運行&#xff0c;無需安裝電腦端。 ?ADB模式&#xff0c;如果你手機沒有root&#xff0c;那你可以windows電腦下載【極限投屏】軟件&#xff0c;然后你的手機和電腦的網絡要同一個wifi&#xff0c;然后把你電腦的ip地址填寫…

【Python】 -- 趣味代碼 - 佩奇

文章目錄 文章目錄 00 佩奇程序設計框架1. 繪圖設置2. 繪制卡通人物的各個部分3. 主程序總結01 佩奇程序設計00 佩奇程序設計框架 這段代碼使用 turtle 模塊繪制了一個粉色的卡通人物圖像,主要功能包括繪制鼻子、頭、耳朵、眼睛、腮、嘴、身體、手、腳和尾巴等部分。代碼的主…

uniapp-商城-69-shop(2-商品列表,點擊商品展示,商品的詳情, vuex的使用,rich-text使用)

頁面中將我們的數據進行了羅列,對于單個數據的展示,還需要進行開發,這里使用了點擊商品后,進行彈窗展示。 同樣這里用一個組件來進行實現該彈窗的展示。 本文介紹了商品詳情彈窗的實現方案。主要采用Vuex進行狀態管理,通過幾個關鍵組件協同工作: 商品列表組件productItem…

C# Datatable篩選過濾各方式詳解

在C#中&#xff0c;DataTable提供了多種篩選過濾數據的方法&#xff0c;以下是常用的幾種方式及其特點&#xff1a; 1. ?Select方法篩選? 這是最基礎的篩選方式&#xff0c;支持類似SQL的表達式語法 // 單條件篩選 DataRow[] rows dt.Select("Age > 25");// …

計算機網絡中的路由算法:互聯網的“路徑規劃師”

計算機網絡中的路由算法&#xff1a;互聯網的“路徑規劃師” 當你打開瀏覽器&#xff0c;輸入 www.example.com 并敲下回車&#xff0c;數據會從你的電腦出發&#xff0c;穿越一個個路由器&#xff0c;最終抵達目標服務器。這一路上&#xff0c;數據包是怎么知道該走哪條路的&…

硬件工程師筆記——三極管Multisim電路仿真實驗匯總

目錄 1 三極管基礎 更多電子器件基礎知識匯總鏈接 1.1 工作原理 NPN型三極管的工作原理 PNP型三極管的工作原理 1.2 三極管的特性曲線 輸入特性曲線 理想和現實輸出特性 三極管的主要參數包括&#xff1a; 2 三極管伏安特性 2.1 伏安特性仿真 Multisim使用說明鏈接…

Linux 進階命令篇

一、Linux 系統軟件安裝命令 &#xff08;一&#xff09;Ubuntu 系統&#xff08;基于 Debian&#xff09; apt &#xff1a;是 Ubuntu 系統中常用的包管理工具&#xff0c;可以自動處理軟件依賴關系。 安裝命令格式 &#xff1a;sudo apt install 軟件名 示例 &#xff1a;…

LVS-DR 負載均衡群集

目錄 一、LVS-DR集群 1、LVS-DR 工作原理 2、數據包流向分析 3、LVS-DR 模式特點 二、直接路由模式&#xff08;LVS-DR&#xff09; 1、準備案例環境 2、配置負載調度器&#xff08;101&#xff09; &#xff08;1&#xff09;配置虛擬IP 地址&#xff08;VIP&#xff…

提升 GitHub Stats 的 6 個關鍵策略

哈哈&#xff0c;GitHub 的 “B-” 評級 其實是個玄學問題&#xff0c;但確實有一些 快速提升的技巧&#xff01;你的數據看起來 提交數&#xff08;147&#xff09;和 PR&#xff08;9&#xff09;不算少&#xff0c;但 Stars&#xff08;21&#xff09;和貢獻項目數&#xff…

常見的垃圾回收算法原理及其模擬實現

1.標記 - 清除&#xff08;Mark - Sweep&#xff09;算法&#xff1a; 這是一種基礎的垃圾回收算法。首先標記所有可達的對象&#xff0c;然后清除未被標記的對象。 缺點是會產生內存碎片。 原理&#xff1a; 如下圖分配一段內存&#xff0c;假設已經存儲上數據了 標記所有…

卷積神經網絡(CNN):原理、架構與實戰

卷積神經網絡&#xff08;CNN&#xff09;&#xff1a;原理、架構與實戰 卷積神經網絡&#xff08;Convolutional Neural Network, CNN&#xff09;是深度學習領域的一項重要突破&#xff0c;特別擅長處理具有網格結構的數據&#xff0c;如圖像、音頻和視頻。自 2012 年 AlexN…

RabbitMQ 集群與高可用方案設計(二)

三、為什么需要集群與高可用方案 &#xff08;一&#xff09;業務需求驅動 隨著業務的快速發展和用戶量的急劇增長&#xff0c;系統面臨的挑戰也日益嚴峻。在這種情況下&#xff0c;對消息隊列的可靠性、吞吐量和負載均衡能力提出了更高的要求&#xff0c;而單機部署的 Rabbi…

《ChatGPT o3抗命:AI失控警鐘還是成長陣痛?》

ChatGPT o3 “抗命” 事件起底 在人工智能的飛速發展進程中&#xff0c;OpenAI 于 2025 年推出的 ChatGPT o3 推理模型&#xff0c;猶如一顆重磅炸彈投入了技術的海洋&#xff0c;激起千層浪。它被視為 “推理模型” 系列的巔峰之作&#xff0c;承載著賦予 ChatGPT 更強大問題解…

RK3568DAYU開發板-平臺驅動開發:I2C驅動(原理、源碼、案例分析)

1、程序介紹 本程序是基于OpenHarmony標準系統編寫的平臺驅動案例&#xff1a;I2C 系統版本:openharmony5.0.0 開發板:dayu200 編譯環境:ubuntu22 部署路徑&#xff1a; //sample/04_platform_i2c 2、基礎知識 2.1、I2C簡介 I2C&#xff08;Inter Integrated Circuit&a…

在UniApp中開發微信小程序實現圖片、音頻和視頻下載功能

隨著微信小程序的迅猛發展&#xff0c;越來越多的開發者選擇通過UniApp框架來進行跨平臺應用開發。UniApp能夠讓開發者在一個代碼庫中同時發布iOS、Android和小程序等多平臺應用。而在實際開發過程中&#xff0c;很多應用都需要實現一些常見的下載功能&#xff0c;例如圖片、音…

鴻蒙5.0項目開發——接入有道大模型翻譯

鴻蒙5.0項目開發——接入有道大模型翻譯 【高心星出品】 項目效果圖 項目功能 文本翻譯功能 支持文本輸入和翻譯結果顯示 使用有道翻譯API進行翻譯 支持自動檢測語言&#xff08;auto&#xff09; 支持雙向翻譯&#xff08;源語言和目標語言可互換&#xff09; 文本操作…

Vim 中設置插入模式下輸入中文

在 Vim 中設置插入模式下輸入中文需要配置輸入法切換和 Vim 的相關設置。以下是詳細步驟&#xff1a; 1. 確保系統已安裝中文輸入法 在 Linux 系統中&#xff0c;常用的中文輸入法有&#xff1a; IBus&#xff08;推薦&#xff09;&#xff1a;支持拼音、五筆等Fcitx&#xf…

湖北理元理律師事務所:債務優化中的“生活錨點”設計

在債務重組領域&#xff0c;一個常被忽視的核心矛盾是&#xff1a;還款能力與生存需求的沖突。過度壓縮生活支出還債&#xff0c;可能導致收入中斷&#xff1b;放任債務膨脹&#xff0c;又加劇精神壓力。湖北理元理律師事務所通過“三步平衡法”&#xff0c;嘗試在法理框架內破…

Prometheus + Grafana 監控常用服務

一、引言 Prometheus監控常見服務的原理主要包括服務暴露指標和Prometheus抓取指標。一方面&#xff0c;被監控服務通過自身提供的監控接口或借助Exporter將服務的性能指標等數據以HTTP協議的方式暴露出來&#xff1b;另一方面&#xff0c;Prometheus根據配置好的采集任務&…

基于YOLOv8 的分類道路目標系統-PyTorch實現

本文源碼: https://download.csdn.net/download/shangjg03/90873939 1. 引言 在智能交通和自動駕駛領域,道路目標分類是一項關鍵技術。通過對攝像頭捕獲的圖像或視頻中的目標進行分類識別,可以幫助車輛或系統理解周圍環境,做出更安全的決策。本教程將介紹如何使用 PyTorch …