手機號登錄與高并發思考

基礎邏輯

一般來說這個驗證碼登錄分為手機號、以及郵箱登錄

手機號短信驗證,以騰訊云SMS 服務為例:

這個操作無非對后端來說就是兩個接口:

一個是獲取驗證碼,這塊后端生成6位數字+expire_time 去推送到騰訊云sdk ,騰訊云sdk再去推送運營商,運營商再去推送到用戶

一個就是驗證驗證碼,根據phone+ expire 去redis查就行了

注意 第一個用戶 使用手機號獲取驗證碼的時候,不要插庫,因為沒有經過驗證,所有涉及到手機號相關的,特別是綁定、登錄等必須要驗證手機號

騰訊云短信sms 服務sdk 封裝,注意python只有同步線程版,故使用asyncio.to_thread 封裝

import asyncio
import jsonfrom tencentcloud.common import credential
from tencentcloud.sms.v20210111 import sms_client, modelsfrom producer.utils.custom_exception_utils import CustomExceptiondef send_sms_sync(phone, code, expiration_date):try:cred = credential.Credential(settings.TENCENT_SMS_SECRET_ID, settings.TENCENT_SMS_SECRET_KEY)client = sms_client.SmsClient(cred, settings.TENCENT_SMS_REGION)req = models.SendSmsRequest()req.SmsSdkAppId = settings.TENCENT_SMS_APP_IDreq.SignName = settings.TENCENT_SMS_SIGN_NAMEreq.TemplateId = settings.TENCENT_SMS_TEMPLATE_IDreq.TemplateParamSet = [code, expiration_date]req.PhoneNumberSet = [phone]resp = client.SendSms(req)logger.info(resp.to_json_string(indent=2))# 將 JSON 字符串轉換為字典r = json.loads(resp.to_json_string())if r["SendStatusSet"][0]["Code"] == "Ok":return relse:error_message = r["SendStatusSet"][0]["Message"]logger.error(f"短信SMS服務失敗:{error_message}")raise CustomException(message=f"{phone}短信SMS服務失敗\n原因:{error_message}")except Exception as e:logger.error(f"發送短信失敗: {str(e)}")raise CustomException(message=str(e))async def send_sms_async(phone, code, expiration_date):result = await asyncio.to_thread(send_sms_sync, phone, code, str(expiration_date))return result

注意先插redis 再發送,因為 發送可能有異常,但是能保證測試的時候redis 有數據

一般來說會以redis 作為expire處理:

    async def get_code(self, telephone: str):"""獲取驗證碼"""code = await self.login_code_services.create_login_code(telephone)await send_sms_async(phone=telephone, code=code, expiration_date=settings.SMS_VALIDATION_CODE_EXPIRATION)

創建六位數字的邏輯

create_login_code的邏輯:

    async def create_login_code(self, telephone: str) -> str:"""根據手機號生成登錄驗證碼并存儲到 Redis 中:param telephone: 用戶手機號:return: 生成的驗證碼"""# 生成驗證碼# 生成驗證碼  generate_code 則是加密函數code = self.generate_code(telephone)# Redis 鍵名(可用手機號做區分)redis_key = f"{self.login_core_prefix}:{telephone}"# 使用 Redis 存儲驗證碼,有效時間 10 分鐘async with redis_client.get_client() as client:try:# 刪除已有的驗證碼await client.delete(redis_key)logger.info(f"Existing login code for {telephone} deleted from Redis.")# 設置新的驗證碼await client.set(redis_key, code, ex=self.login_code_expiration)logger.info(f"New login code for {telephone} stored in Redis: {code}")except Exception as e:logger.error(f"Failed to store login code in Redis: {e}")raisereturn code

以上展示的是最簡單的驗證登錄(設計sdk + 簡單的expire 處理)

高并發請求限制

1、問題:假設你的系統面對高并發用戶時,短信驗證碼的請求頻率可能會非常高。如何防止惡意用戶利用暴力破解或刷驗證碼的方式發起過多請求?

考察點

? 防止頻繁請求(限流)。

? 防止濫用驗證碼接口。

解決方案

? 限流機制:使用 Redis 或類似工具實現用戶請求頻率限制。比如,使用令牌桶算法(Token Bucket)或者漏桶算法(Leaky Bucket)來限制每個手機號每分鐘的驗證碼請求次數。

令牌桶

Token Bucket 令牌桶 [適用于需要平滑請求速率的場景,特別是在高并發的情況下,它可以平衡請求流量。]

令牌桶 本質:

先查redis 對應的鍵里面的值 的長度 是否超標了

超標了 就refuse 否則就是pass

插入redis的邏輯: 使用zset (有序集合)在[0, time] 插入值

import redis
import timeredis_client = redis.StrictRedis(host='localhost', port=6379, db=0)# 配置
max_tokens = 5  # 最大令牌數
rate = 1  # 每秒生成一個令牌,每秒只能插入一個新的令牌時間戳(即每秒最多允許一個請求),
# 如果超出這個速率,后續的請求就會被拒絕。
interval = 60  # 限流時間窗口,單位為秒def generate_token(phone_number):# 構造桶鍵bucket_key = f"sms_rate_limit:{phone_number}"# 獲取當前時間(秒),這個就是rate 內容current_time = int(time.time())# 清除過期的令牌# ?	這行代碼用來移除掉超過時間窗口 interval 的過期令牌。# 如果設置的是 interval=60,那么每次請求時都會移除掉超過 60 秒的令牌,# 確保令牌桶中只包含當前時間窗口內的令牌。# 刪除 score 在 [0, current_time - interval] 之間的所有元素。redis_client.zremrangebyscore(bucket_key, 0, current_time - interval)# 獲取當前桶內的令牌數(即時間戳數)tokens = redis_client.zrange(bucket_key, 0, -1)# 如果桶里有足夠的令牌,則拒絕請求if len(tokens) >= max_tokens:return False  # 限流拒絕else:# 向桶中添加當前時間戳作為新的令牌redis_client.zadd(bucket_key, {current_time: current_time})return True  # 允許請求def check_rate_limit(phone_number):# 獲取桶的令牌數量bucket_key = f"sms_rate_limit:{phone_number}"tokens = redis_client.llen(bucket_key)# 如果桶內令牌超過最大容量,表示請求超限if tokens >= max_tokens:return False  # 拒絕請求else:return True  # 允許請求

漏桶算法

漏桶算法 (Leaky Bucket)

它的水流速率是固定的,水桶有固定容量。當水桶滿了,任何新的請求都會被丟棄。[適用于流量穩定的場景,具有更強的固定速率處理能力。]

import redis
import timeredis_client = redis.StrictRedis(host='localhost', port=6379, db=0)# 配置
bucket_key = "sms_rate_limit:phone_number"
max_capacity = 5  # 桶的容量,最大允許的請求數
rate = 1  # 處理請求的速率(每秒處理一個請求)def process_request(phone_number):bucket_key = f"sms_rate_limit:{phone_number}"# 當前時間current_time = int(time.time())# 清理過期請求(處理漏桶)redis_client.zremrangebyscore(bucket_key, 0, current_time - 60)# 判斷請求是否超出容量限制if redis_client.zcard(bucket_key) >= max_capacity:return False  # 超出請求限制,拒絕請求# 添加當前請求時間戳redis_client.zadd(bucket_key, {current_time: current_time})return True  # 允許請求

,但漏桶著重于處理速率限制和固定容量控制,而令牌桶則關注請求的流量速率和生成令牌的動態過程。

滑動窗口限流

? 驗證碼有效期:設置合理的驗證碼過期時間(通常是 3-5 分鐘),避免用戶在很長時間內嘗試。

? 滑動窗口限流:每次請求時,記錄用戶請求的時間戳,在每次請求時檢查用戶在最近一分鐘內的請求次數,如果超過限制,則拒絕該請求。

# 假設我們用 Redis 記錄每個手機號的請求次數
import redis
from time import timeredis_client = redis.StrictRedis(host='localhost', port=6379, db=0)def check_sms_limit(phone_number):key = f"sms_request_count:{phone_number}"current_time = int(time())expire_time = 60  # 1 minutemax_requests = 5  # 最大請求次數# 檢查手機號最近一段時間內的請求次數requests = redis_client.lrange(key, 0, -1)requests = [int(r) for r in requests]# 刪除過期請求(超過1分鐘的請求)requests = [r for r in requests if r > current_time - expire_time]if len(requests) >= max_requests:return False  # 超過最大請求次數# 添加當前請求時間redis_client.rpush(key, current_time)redis_client.expire(key, expire_time)return True

驗證碼泄漏

2、問題:用戶的手機可能存在被盜的風險,如何結合其他認證機制提高安全性?比如通過 動態密碼生物識別 進一步加強登錄安全性。

考察點

? 多因素認證(MFA)。

? 雙重驗證的實現。

雙重驗證(2FA)方案,結合了 一次性密碼(OTP)二維碼生成 來增強系統的安全性。這個過程通常分為兩個步驟:

  1. 生成一次性密碼:通過一個標準的算法(如 TOTP)生成一次性密碼。

  2. 二維碼展示與掃描:將 OTP 所需的密鑰(通常是一個隨機生成的密鑰)通過二維碼的方式呈現給用戶,用戶可以使用 TOTP 兼容的應用(如 Google Authenticator 或 Authy)來生成驗證碼。

使用 TOTP (基于時間的一次性密碼) 生成 OTP

TOTP(Time-based One-Time Password)算法基于時間生成一次性密碼,通常使用 HMAC-SHA1 算法。這個算法確保了每隔一段時間生成一個新的密碼。

Time-Based One-Time Password

import pyotp
import qrcode
from hashlib import sha256# 假設用戶的手機號是唯一標識符
user_phone_number = "13800000000"# 使用手機號作為種子生成唯一的 secret
# 使用 hashlib 將手機號進行哈希處理,生成一個固定的 secret
# 這樣即使服務重啟,每次生成的 secret 都是一樣的
secret = pyotp.random_base32()  # 你可以先生成一個固定的 secret, 并保存到數據庫# 如果希望生成基于手機號的 secret,可以使用 hashlib 和手機號
hashed_phone = sha256(user_phone_number.encode()).hexdigest()
secret = hashed_phone[:16]  # 使用手機號的哈希值的一部分作為 secret# 將用戶手機號與生成的 secret 綁定存儲(此處示例,實際應存數據庫)
user = {"phone_number": user_phone_number,"2fa_enabled": True,"2fa_secret": secret
}# 生成二維碼URL
totp = pyotp.TOTP(secret)
uri = totp.provisioning_uri(user_phone_number, issuer_name="YourApp")# 生成二維碼(可以通過Web頁面顯示)
qr = qrcode.make(uri)
qr.show()  # 展示二維碼# 用戶掃碼后,輸入驗證碼(假設用戶輸入了 '123456')
user_input_otp = "123456"# 驗證用戶輸入的OTP是否有效
if totp.verify(user_input_otp):print("2FA Verification Success")
else:print("Invalid OTP")

驗證碼頻繁失效

問題:驗證碼的過期時間通常比較短,但某些場景下可能會發生驗證碼失效,用戶卻沒有及時看到短信,如何處理這種情況?

延長驗證碼的過期時間雖然能夠解決部分問題,但帶來的一些安全和性能隱患也是不容忽視的。最好的解決方案是在驗證碼過期之前提供驗證碼重發、動態刷新或者適當的過期提醒等方式來保障用戶的體驗,同時確保系統的安全性和效率。

跨設備驗證碼

問題:同一個用戶在不同設備上登錄時,可能會因為設備間驗證碼的同步問題導致登錄失敗或需要重復輸入驗證碼。如何在跨設備的場景下保證一致性?

考察點

? 跨設備的一致性。

? 設備間驗證碼的共享和同步。

. 生成設備標識(Device ID)

每次用戶登錄時,前端可以生成一個唯一的設備標識(Device ID),并將其作為參數與驗證碼一起發送到后端。這個設備標識可以基于設備的硬件信息、安裝的應用ID,或者生成一個唯一的UUID。比如可以通過瀏覽器的 localStorage、sessionStorage 或者移動端的設備ID生成。

2. 發送驗證碼時包含設備標識

后端在發送驗證碼時,將設備標識與手機號和驗證碼一起存儲在 Redis 或數據庫中。這樣,無論用戶在哪個設備上獲取驗證碼,都能根據設備標識進行有效的關聯。

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

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

相關文章

Python設計模式 - 適配器模式

定義 適配器模式(Adapter Pattern)是一種結構型設計模式,它用于將一個類的接口轉換為客戶端所期待的另一個接口。 注:在適配器模式定義中所提及的接口是指廣義的接口,它可以表示一個方法或者一組方法的集合。 結構 …

【前端工程化】

目錄 前端工程戶核心技術之模塊化前端模塊化的進化過程commonjs規范介紹commonjs規范示例commonjs模塊打包 amd規范、cmd規范前端工程化關鍵技術之npmwebpack原理 前端工程戶核心技術之模塊化 前端模塊化是一種標準,不是實現。commonjs是前端模塊化的標準&#xff…

關于CNN,RNN,GAN,GNN,DQN,Transformer,LSTM,DBN你了解多少

以下是神經網絡中常見的幾種模型的簡要介紹: 1. ?CNN (Convolutional Neural Network, 卷積神經網絡) ?用途: 主要用于圖像處理和計算機視覺任務。?特點: 通過卷積核提取局部特征,具有平移不變性,能夠有效處理高維數據(如圖像…

T113-i開發板的休眠與RTC定時喚醒指南

??在嵌入式系統設計中,休眠與喚醒技術是優化電源管理、延長設備續航的關鍵。飛凌嵌入式基于全志T113-i處理器開發設計的OK113i-S開發板提供了兩種休眠模式:freeze和mem,以滿足不同應用場景下的功耗與恢復速度需求。本文將詳細介紹如何讓OK1…

SpringBoot項目實戰(初級)

目錄 一、數據庫搭建 二、代碼開發 1.pom.xml 2.thymeleaf模塊處理的配置類 3.application配置文件 4.配置(在啟動類中) 5.編寫數據層 ②編寫dao層 ③編寫service層 接口 實現類 注意 補充(注入的3個注解) 1.AutoWir…

高性能網絡SIG雙月動態:加速 SMC eBPF 透明替換特性上游化進程,并與上游深度研討新特性

01、整體進展 本次雙月報總結了 SIG 在 1 月和 2 月的工作進展,工作聚焦在 ANCK CVE 和穩定性問題修復,以及上游 SMC eBPF 透明替換特性推進和多個話題討論上。 本月關鍵進展: 1. 推進 SMC eBPF 透明替換特性上游化,更新至 V7&…

某視頻的解密下載

下面講一下怎么爬取視頻,這個還是比小白的稍微有一點繞的 首先打開網址:aHR0cDovL3d3dy5wZWFydmlkZW8uY29tL3BvcHVsYXJfNA 首頁 看一下: 有一個標題和一個href,href只是一個片段,待會肯定要拼接, 先找一…

C++繼承機制:從基礎到避坑詳細解說

目錄 1.繼承的概念及定義 1.1繼承的概念 1.2 繼承定義 1.2.1定義格式 1.2.2繼承關系和訪問限定符 1.2.3繼承基類成員訪問方式的變化 總結: 2.基類和派生類對象賦值轉換 3.繼承中的作用域 4.派生類的默認成員函數 ?編輯 默認構造與傳參構造 拷貝構造&am…

測試基礎入門

文章目錄 軟件測試基礎1.1軟件測試概述什么是軟件測試什么是軟件需求說明書軟件測試的原則測試用例的設計測試用例設計的基本原則軟件測試分類軟件缺陷的定義 2.1軟件開發模型軟件開發模型概述大爆炸模型(邊寫邊改)軟件開發生命周期模型--螺旋模型軟件開…

022-spdlog

spdlog 以下是從原理到代碼實現的全方位spdlog技術調研結果,結合核心架構、優化策略和完整代碼示例: 一、核心架構設計原理 spdlog三級架構 (圖示說明:spdlog采用三級結構實現日志系統解耦) Registry管理中樞 全局…

STM32時鐘樹

時鐘樹 時鐘樹就是STM32中用來產生和配置時鐘,并且把配置好的時鐘發送到各個外設的系統,時鐘是所有外設運行的基礎,所以時鐘也是最先需要配置的東西,在程序中主函數之前還會執行一個SystemClock_Config()函數,這個函數…

【第22節】windows網絡編程模型(WSAAsyncSelect模型)

目錄 引言 一、WSAAsyncSelect模型概述 二、WSAAsyncSelect模型流程 2.1 自定義消息 2.2 創建窗口例程 2.3 初始化套接字 2.4 注冊網絡事件 2.5 綁定和監聽 2.6 消息循環 三、完整示例代碼 引言 在網絡編程的廣袤天地中,高效處理網絡事件是構建穩定應用的…

利用Dify編制用戶問題意圖識別和規范化回復

繼上一篇文章,成功完成Dify本地部署后,主要做了一些workflow和Agent的應用實現,整體感覺dify在工作流可視化編排方面非常好,即使部分功能無法實現,也可以通過代碼執行模塊或者自定義工具來實現(后續再具體分…

雙核鎖步技術在汽車芯片軟錯誤防護中的應用詳解

摘要 本文深入探討了雙核鎖步技術在保障汽車芯片安全性中的應用。文章首先分析了國產車規芯片在高安全可靠領域面臨的軟錯誤難點及攻克方向,然后詳細介紹了雙核鎖步技術的基本原理及其在汽車芯片防軟錯誤的重要性。通過對比國內外多家廠商的芯片技術,分析…

Lustre 語言的 Rust 生成相關的工作

目前 Lustre V6 編譯器支持編譯生成的語言為C語言。但也注意到,以 Rust 語言為生成目標語言,也存在若干相關工作。 rustre(elegaanz) 該項工作為 Lustre v6 語言的解析器,使用 Rust 語言實現。生成 Lustre AST。 項…

Java 之「單調棧」:從入門到實戰

Java 單調棧:從入門到實戰 文章目錄 Java 單調棧:從入門到實戰引言什么是單調棧?單調遞增棧單調遞減棧 單調棧的應用場景Java 實現單調棧代碼示例:下一個更大元素代碼解析 單調棧的優勢實戰應用:股票價格跨度代碼示例代…

【Golang】defer與recover的組合使用

在Go語言中,defer和recover是兩個關鍵特性,通常結合使用以處理資源管理和異常恢復。以下是它們的核心應用場景及使用示例: 1. defer 的應用場景 defer用于延遲執行函數調用,確保在函數退出前執行特定操作。主要用途包括&#xff…

CSS 中flex - grow、flex - shrink和flex - basis屬性的含義及它們在彈性盒布局中的協同作用。

大白話CSS 中flex - grow、flex - shrink和flex - basis屬性的含義及它們在彈性盒布局中的協同作用。 在 CSS 的彈性盒布局(Flexbox)里,flex-grow、flex-shrink 和 flex-basis 這三個屬性對彈性元素的尺寸和伸縮性起著關鍵作用。下面為你詳細…

OpenGL ES ->乒乓緩沖,計算只用兩個幀緩沖對象(Frame Buffer Object)+疊加多個濾鏡作用后的Bitmap

乒乓緩沖核心思想 不使用乒乓緩沖,如果要每個濾鏡作用下的繪制內容,也就是這個濾鏡作用下的幀緩沖,需要創建一個Frame Buffer Object加上對應的Frame Buffer Object Texture使用乒乓緩沖,只用兩個Frame Buffer Object加上對應的F…

【HarmonyOS NEXT】關鍵資產存儲開發案例

在 iOS 開發中 Keychain 是一個非常安全的存儲系統,用于保存敏感信息,如密碼、證書、密鑰等。與文件系統不同,Keychain 提供了更高的安全性,因為它對數據進行了加密,并且只有經過授權的應用程序才能訪問存儲的數據。那…