網站首頁 編程語言 正文
前言:
晚上打算睡覺的時候,群里反饋訂單接收失敗,開工排查問題,日志顯示驗簽失敗,發現一個蠻有意思的BUG,總算有了一個寫作的素材
場景描述
本次的場景屬于比較常見的收單API,對第三方的訂單進行簽名驗證,然后持久化到數據庫,簽名規則大致是將參數key按照升序排序,然后根據key=value&進行字符串拼接,最后加上秘鑰,按照指定的加密方式生成簽名
前戲一
設計之初,肯定是怎么簡單怎么來,粗略代碼如下
[HttpPost]
public async Task<IActionResult> TestSendOrder([FromBody] ReceiveOrderRequest request)
{
var secret_key = _options.Value.SecretKey;
var url = _options.Value.Host;
//1.將模型轉成json格式字符串
var param = JsonConvert.SerializeObject(request);
//2.將json格式字符串,序列化成有序字典
SortedDictionary<string, string> dict = JsonConvert.DeserializeObject<SortedDictionary<string, string>>(param);
//3.循環字典,按規則拼接成待加密的明文字符串
var data = "";
foreach (var item in dict)
{
if (item.Key == "sign") continue;
data += $"{item.Key}={item.Value}&";
}
data += $"secret_key={secret_key}";
//4.生成簽名
var sign = EncryptHelper.SHA1Encryption(data);
request.sign = sign;
//5.模擬訂單推送
var res = await _httpClientHelper.PostData(url, JsonConvert.SerializeObject(request));
return Ok(res);
}
不出意外,肯定是要出意外的,聯調的時候,發現與第三方待加密的明文字符串不一致,問題出在JsonConvert序列化上,這里有兩個問題
1. DateTime格式不一致
如: DateTime dt = "2022-07-30 12:26:56"
序列化后 dt=2022-07-30T12:26:56
2. decimal小數點后自動補0
如: decimal price = 10
序列化后 price=10.0
針對第一個問題,很好解決,我們在序列化的時候,指定DateTime的格式即可
var iso = new IsoDateTimeConverter();
iso.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
var param = JsonConvert.SerializeObject(request, iso);
針對第二個問題,處理起來就比較麻煩了,要重寫底層的一些東西(主要是我不會),這不符合"簡單"的定義,得換個方案
前戲二
通過反射遍歷對象,然后將屬性名稱與值,丟到有序字典里面,這里我寫了個方法來判斷值是否為時間,如果是時間類型,則格式化,代碼如下
public string GetFmortDateTime(string strDate)
{
DateTime dt;
if (DateTime.TryParse(strDate, out dt))
{
return dt.ToString("yyyy-MM-dd HH:mm:ss");
}
else
{
return strDate;
}
}
不出意外,肯定是要出意外的,不然也不會有這個素材去水一篇博客了
正戲
有個字段的值是9.9,結果被序列化成了 2022-09-09 00:00:00,吃了一驚,看來是把這個數字格式化成月份日份了,真有意思,又GET到一個新姿勢,發現問題解決問題就簡單多了,因為定義了數據模型,我們直接在反射的時候,獲取該值的類型做判斷即可
public static async Task<bool> CheckSign(dynamic request, string secret)
{
SortedDictionary<string, string> dict = new SortedDictionary<string, string>();
foreach (PropertyInfo p in request.GetType().GetProperties())
{
var value = p.GetValue(request);
if (value == null)
{
dict[p.Name] = "";
}
else
{
var valueType = value.GetType();
if (valueType.Name == "DateTime")
{
dict[p.Name] = Convert.ToDateTime(value).ToString("yyyy-MM-dd HH:mm:ss");
}
else
{
dict[p.Name] = value.ToString();
}
}
}
var sign = dict["sign"];
dict.Remove("sign");
var data = "";
foreach (var item in dict)
{
data += $"{item.Key}={item.Value}&";
}
data += $"secret_key={secret}";
var new_sign = EncryptHelper.SHA1Encryption(data);
return new_sign.ToLower() == sign.ToLower();
}
尾戲
看到這里,可能就有小伙伴有話要說了,你這定義了一個模型,還要通過循環兩次,才能生成待加密的明文字符串,不符合"簡單",干脆直接用個有序字典去接收參數好了,這樣只用循環一次
秒啊,秒啊,秒啊,妙蛙種子都沒有你秒,這種做法不是不行,但是后面維護的人估計要抓狂了,按照規約,我們是不推薦這么干的,這次就破例這么干一次,拋出另一個問題,一個字符串,如何判斷它是一個我們約定的時間格式,很顯然9.9并不是約定的時間格式
這里推薦 DateTime.ParseExact方法,可以根據我們自定義的方式,來格式化時間,舒坦了...
public static string GetFmortDateTime(string strDate)
{
string[] format = { "yyyy-MM-ddTHH:mm:ss" };
DateTime dt;
if (DateTime.TryParseExact(strDate,format,CultureInfo.InvariantCulture,DateTimeStyles.None,out dt))
{
return dt.ToString("yyyy-MM-dd HH:mm:ss");
}
else
{
return strDate;
}
}
原文鏈接:https://www.cnblogs.com/tibos/p/16543880.html
相關推薦
- 2022-08-28 前端實現加載完畢之前loading提示效果
- 2023-01-15 PyQt5+QtChart實現柱狀圖的繪制_python
- 2021-12-09 golang中gin框架接入jwt使用token驗證身份_Golang
- 2022-08-08 Android實現頁面跳轉_Android
- 2022-05-07 Python數據結構之棧詳解_python
- 2022-04-12 C++中標準線程庫的基本使用介紹_C 語言
- 2023-10-24 記ElementUI內置的$confirm確認消息彈框方法
- 2023-01-07 Python中層次聚類的詳細講解_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支