想象一下這個場景。客戶收到你的預約提醒短信,隨手回復了一句“確認”或者“能改時間嗎?”又或者有人發消息到你的客服號碼,想問問訂單進展。再比如某個用戶在進行驗證時直接回了條消息提問。這些回復背后,需要有個東西接收短信、理解當前的對話語境,給出合適的回應。這就正是“單向短信群發”和“真正建立對話”之間的鴻溝。
兩種啟動方式都挺常見的。一是客戶先發來消息,可能是問問題、求助,或者對某個通知的回復。另一種是系統先發出第一條短信,像是預約提醒、驗證碼、訂單狀態更新。無論哪一邊先開口,客戶想回都能回。兩條路徑共用同一套接收地址,區別只在于第一個消息是應用發出的,還是客戶主動發來的。
![]()
我們做的這套東西,是用AWS無服務器架構搭配Sinch對話接口來跑通的。如果你看過這個系列之前的內容,應該會認出那個參數存儲憑證模式。當然,沒看過也不影響,這篇完全能獨立往下看。
先說一句,圖上沒畫死信隊列,但兩個隊列都有。消息如果重試三次還是失敗,就會被丟進對應的死信隊列里,等人來排查。
整個系統分成幾個角色明確的模塊。發件函數負責從外發隊列里讀消息,通過對話接口把短信發出去,然后用這條消息的編號去存對話記錄。不管是應用自動觸發的、人工客服發的,還是處理器生成的回復,最后都要經過它才能變成真正的短信到達客戶手機。
接收函數只干幾件事:校驗消息的散列簽名,把發件人、文本內容、對話編號這些字段從請求里拎出來,丟進收件隊列,之后立刻返回200狀態碼。這么快就結束調用,是因為不能讓后面的處理拖慢響應——它自己從不失敗,因為它壓根不碰那些耗時的復雜邏輯。
處理器函數從收件隊列里拿出消息,調用一個專門的方法來決定該回復什么內容,然后把準備好的回復放進外發隊列。它有長達60秒的時間慢慢處理,而且在這個過程中完全不需要拿著Sinch的接口憑證。萬一處理失敗,隊列服務會自動發起重試。
這樣一來,你的處理器連Sinch接口的邊都沾不上。人工客服、外部的支撐工具、或者其它任何系統,想發消息都可以往同一個外發隊列里丟。消息的入口統一了,但背后的來源可以是五花八門的。
接收函數的核心職責很純粹:驗證簽名,拒絕掉超出時間窗口的舊請求,然后入隊。至于上下文理解、內容決策那些事,一概不參與。
這里有個小細節挺關鍵。函數地址用的是無需認證的模式,因為Sinch那邊發請求過來的時候,沒辦法帶上AWS自己的身份驗證頭。于是安全校驗就全靠散列簽名來兜底。它要同時確認兩件事:請求里攜帶的內容沒有被中途篡改過,以及這條請求的發送時間足夠“新鮮”。原理是,請求頭里附帶的時間戳和消息體一起參與了簽名運算,所以攻擊者如果想把截獲到的請求在五分鐘窗口關閉后重放,簽名就匹配不上了。反過來,如果沒有這兩層驗證,任何人只要知道接口地址就能隨便往里塞數據,那安全性自然無從談起。
代碼里引入的模塊也印證了這套校驗流程:先是拿密碼學模塊里的散列創建和恒定時間比較函數來做簽名比對,接著通過參數存儲加載線上保管的簽名密鑰,然后在構造入隊指令時把目標隊列地址傳進去。那個300秒的時間容差值,就是剛才說的五分鐘窗口,專門用來判定請求是否已經過期。
換個角度看,這套設計等于是把簽名驗證這件安全臟活,從你的業務處理器里完整剝離了出來。處理器只用關心對話邏輯本身,不用分心去管請求是不是偽造的、有沒有被篡改、時間是否過期。這種關注點分離讓每個模塊的邊界都很干凈。
如果你在搭建需要和客戶進行雙向短信溝通的內部工具、客服系統或者自動化流程,這種將簽名校驗前置、收件隊列緩沖、處理器獨立決策的模式,能讓整條鏈路清晰不少。畢竟,短信一旦發出去了,客戶真的會回,而你能接住并回應,才算真正擁有對話能力。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.