ASP.NET沒有魔法——ASP.NET Identity的加密與解密

来源:http://www.cnblogs.com/selimsong/archive/2017/11/03/7771875.html
-Advertisement-
Play Games

前面文章介紹瞭如何使用Identity在ASP.NET MVC中實現用戶的註冊、登錄以及身份驗證。這些功能都是與用戶信息安全相關的功能,數據安全的重要性永遠放在第一位。那麼對於註冊和登錄功能來說要把密碼及用戶其它信息通過表單的形式安全的提交到伺服器上,那麼最適合的方法就是使用HTTPS(如果有條件或 ...


   前面文章介紹瞭如何使用Identity在ASP.NET MVC中實現用戶的註冊、登錄以及身份驗證。這些功能都是與用戶信息安全相關的功能,數據安全的重要性永遠放在第一位。那麼對於註冊和登錄功能來說要把密碼及用戶其它信息通過表單的形式安全的提交到伺服器上,那麼最適合的方法就是使用HTTPS(如果有條件或者有安全需求,應該所有請求都基於HTTPS,本章不涉及HTTPS的介紹),而在註冊時用戶的密碼應該加密後保存在資料庫中,包括登錄時對用戶名的驗證也是對密碼明文加密後再進行匹配,對於身份驗證來說,伺服器生成的用戶信息字元串是必須進行加密的,其目的是保護用戶信息並且能夠讓當前的伺服器(或集群)能夠識別。

    本章將從以下幾點對Identity中涉及到的加解密進行介紹:
  ● 常用的加密方法
  ● .Net對常用加密方法的實現
  ● Identity用戶密碼的加解密
  ● Identity用戶身份信息的處理過程
  ● MachineKey的加解密
  ● 自定義Identity身份信息的驗證(基於MachineKey)

常用的加密方法

  軟體中常用的加密方法分為兩類,一類是密文可解密回明文的,而另一類是密文不可解密的。
  對於可解密的這一類主要是通過對稱加密演算法非對稱加密演算法,如DES、AES、RSA等,它們最主要的特點是需要“密鑰”來進行加解密工作,如果密鑰泄露了,那麼就會造成安全問題。
  而不可解密的這一類主要是通過MD5、SHA1這些單向Hash演算法來提取“信息指紋”,已達到“加密”的效果,但這種方法也存在缺陷就是只要演算法相同,那麼對同一個字元串加密後的結果就是相同的,當黑客拿到了用戶資料庫,雖然用戶密碼是被加密存儲,但是黑客可以通過建立“彩虹表”的方式破解密碼。所以又出現了一種通過加“鹽”的演算法,通過加入特殊的“鹽”來保證相同HASH演算法相同字元串的加密不一致性,但如果“鹽”泄露了黑客仍然能夠破解,所以又有了“隨機鹽”。
  參考:http://mp.weixin.qq.com/s?__biz=MzIwMzg1ODcwMw==&mid=2247486407&idx=1&sn=51dfbce7d04ab6faeb0f5a27a5bdcbf8&source=41#wechat_redirect

.Net對常用加密方法的實現

  .Net的System.Security.Cryptography命名空間下包含了用於加解密的類型,這些類型有些是基於托管代碼的,有些是基於Windows API的。
  .Net的加解密類型位於system.dll程式集中(註:非windows平臺下可以通過nuget安裝System.Security.Cryptography.Primitives.dll)的System.Security.Cryptography命名空間下。並且將加密演算法分為了三類:
  ● 對稱加密演算法:AES、DES等
  ● 非對稱加密演算法:RSA、ECDH 等
  ● HASH演算法:SHA1、MD5等
  另外要註意的是.Net中使用面向數據流的方式實現了對稱加密和HASH演算法。這樣的設計可以通過串聯的方式將多個加密演算法合併在一起對“數據流”進行操作(這個東西有點類似於owin中間件的方式,可以根據需求動態的對數據加密進行處理)。
  微軟官方文檔對加密演算法的使用推薦:
  ● 數據保護:Aes
  ● 數據完整性:HMACSHA256、HMACSHA512
  ● 數字簽名:ECDsa、 RSA
  ● 密鑰交換:ECDiffieHellman、 RSA
  ● 隨機數生成:RNGCryptoServiceProvider
  ● 通過密碼生成Key(使用隨機鹽的Hash演算法):Rfc2898DeriveBytes
  更多信息參考文檔:https://docs.microsoft.com/en-us/dotnet/standard/security/cryptography-model

Identity用戶密碼的加解密

  用戶的密碼一般來說是一個長度較短的包含各種字元的字元串,而對用戶密碼加密的目的是避免用戶密碼在資料庫中明文存儲,明文存儲密碼會導致系統開發或運營人員對用戶信息安全的威脅以及黑客攻擊數據泄露導致的用戶信息安全。所以一般來說加密密碼使用無法解密的Hash演算法“加密”
  根據前面文章的分析得知,用戶的創建和密碼的匹配都是通過Identity中的UserManager類型完成的:
  1. 註冊調用的代碼:

  

  2. 登錄調用的代碼(註:SignInManager位於Microsoft.AspNet.Identity.Owin程式集中):

  

  但通過查看源碼可知,SignInManager實際上也是通過UserManager來匹配密碼的:

  

  3. 所以根據上面的分析,用戶密碼的加密是在UserManager中完成的,而UserManager定義中有一個IPasswordHasher的介面,該介面定義了密碼的Hash加密以及Hash後的密碼校驗

  

  IPasswordHasher的預設實現是PasswordHasher類型:

  

  從代碼中可以看到PassswordHasher又是通過一個名稱為Crypto類型的靜態方法完成加密和驗證的:

  Hash計算:

  

  Hash驗證: 

   

  從Crypto的代碼中可以得出以下幾點結論:
  1. Identity中預設的密碼加密基於Rfc2898演算法(通過隨機鹽以及設置迭代次數來計算hash值的演算法)。
  2. 演算法中的“鹽”長度為16位,迭代計算次數為1000次(註:每次實例化Rfc2898DeriveBytes類型時會根據鹽的長度,創建一個隨機的數組。Rfc2898DeriveBytes的GetBytes的演算法不在此詳解,有興趣可參考文檔和源碼)。
  3. 加密時Identity將鹽和加密後的結果進行了拼接,前16位數據為鹽後面的是密碼加密結果。
  4. 密碼的“解密”實際上是通過已經加密的結果先獲取其前16位數據拿到鹽,然後再對傳入的密碼和這個鹽進行一次Hash,然後比較兩次的Hash結果是否相同(註:Hash演算法無法解密)。
  如果要對Identity中的用戶密碼加密演算法進行變更或者擴展,僅需實現新的IPasswordHasher,然後在創建UserManager實例時將其替換即可。
  註:事實上如果黑客拿到了上面數據理論上仍舊是可以破解密碼的,但由於鹽是隨機的,所以導致大批量破解會更加麻煩,這樣哪怕數據泄露了也有時間進行一些補救,所以Rfc2898是一種常用的密碼加密方式。

 Identity用戶身份信息的處理過程

  Identity的用戶身份信息相對於密碼來說要複雜很多,因為密碼僅僅是一個字元串,對一個字元串的加解密很容易,但是Identity的用戶身份信息實際上是一個AuthenticationTicket實例:

  

  那麼Identity是如何對這個用戶身份信息實例進行處理的呢?

  1. 首先我們知道的是Identity通過app.UseCookieAuthentication方法在管道中添加了一個類型為CookieAuthenticationMiddleware的中間件,而通過對源碼分析可以看到,該中間件中實際上是通過創建一個名為CookieAuthenticationHandler的內部類型,通過這個類型完成了請求時Cookie的獲取、驗證,驗證失敗的跳轉以及響應時Cookie的寫入等功能。

  其中Cookie的加解密代碼如下:

  解密:先獲取Cookie值,然後通過TicketDataFormat的Unprotect方法返回一個AuthenticationTicket實例:

  

  加密:將AuthenticationTicket實例通過TicketDataFormat的Protect方法轉換為一個加密後的字元串。

  

  2. Identity對用戶身份信息的處理主要是通過TicketDataFormat完成,從上面代碼中可以看到TicketDataFormat是來來自Options。這裡的Options實際上就是app.UseCookieAuthentication方法中的參數CookieAuthenticationOptions:

  

  TicketDataFormat預設值是在構造方法中創建的,它需要一個protector(註:Protector實際上就是加解密的組件,本章後面詳解)  

  

  3. TicketDataFormat的職責:

  由於TicketDataFormat是繼承於SecureDataFormat類型,並且僅僅是在構造方法中硬編碼了傳入基類的參數,所以其功能實際上是基類實現的:

  

  職責一:數據“保護”,先通過序列化器將泛型類型TData進行序列化(這裡的TData實際上是AuthenticationTicket類型),然後通過加密組件對序列化後的二進位進行加密,最後通過編碼器將二進位數據轉換為Base64Url字元串,代碼如下圖:

  

  這裡要註意以下兩點:

    1). 序列化器是由TicketDataFormat構造方法中硬編碼的,其真實類型為TicketSerializer(對於序列化這個概念,實際上就是將一個程式中的記憶體實例,用二進位數據或者XML、Json等方式保存下來,然後需要使用的時候在通過這些數據把它反序列化為之前的記憶體實例,這裡的TicketSerializer是一個二進位序列化器):

    

    2). 編碼器的名稱為Base64Url與Base64編碼器的區別是,由於Base64字元串中可能會存在斜杠(/)等特殊符號,但是這些符號在url中是無法被正確識別的,所以Base64Url對這些字元進行了特殊處理:

    

  職責二:數據的“解保護”實際上就是保護功能反過來:先將Base64Url字元串解碼為二進位數據,然後對二進位數據解密,最後對解密後的數據進行反序列化:

  

  而本章的重點實際上是在數據的加解密上,所以protector才是關註重點,這裡的protector從上面的代碼中可以看到是通過IAppBulider創建的:

  

  前面的文章分析過,Owin的核心實際上是一個字典,所以通過Owin來獲取的東西應該是保存在字典中的:

  

  AppBuilder的初始化代碼:

  

  根據上面的分析得出,在沒有指定特殊的數據保護器情況下,Identity使用MachineKeyDataProtector作為預設的數據保護器。

  補充說明:

  Identity中的身份驗證的原理,實際上是獲取到Cookie成功解密並反序列化為AuthenticationTicket實例後,將通過身份驗證的Identity(該Identity中的IsAuthenticated屬性為true)信息添加到HTTP請求的上下文中的。MVC中需要通過身份驗證的訪問控制就是通過請求上下文中Identity的IsAuthenticated屬性完成判斷的。

  

MachineKey的加解密

  .Net中有一個名為MachineKey的組件,它用於Forms驗證用戶信息、asp.net 的View State以及跨進程的會話狀態數據的加密和驗證,MachineKey可以通過在web.config文件中加入以下的配置文件來對MachineKey的加解密、驗證演算法及其密鑰進行配置,詳情可參考文檔:https://msdn.microsoft.com/en-us/library/w8h3skw9(v=vs.100).aspx

  

  而上面分析知道Identity使用MachineKeyDataProtector作為數據保護器,而MachineKeyDataProtector實際上使用的就是MachineKey:

  

  註:由於MachineKey相關代碼比較複雜,本文中僅對其主要的一些對象以及加解密過程進行介紹:

  MachineKey的主要相關對象: 

  ● AspNetCryptoServiceProvider(內部類型):ASP.NET用其獲取適合的加密組件。
  ● MachineKeySection:用於表示MachineKey的配置信息。
  ● MachineKeyCryptoAlgorithmFactory(內部類型):MachineKey的加密演算法工廠,依賴MachineKeySection,可以從配置文件中獲取加密演算法類型。
  ● MachineKeyMasterKeyProvider(內部類型):密鑰提供器,依賴MachineKeySection,可以從配置文件中獲取密鑰信息。
  ● MachineKeyDataProtectorFactory (內部類型):數據保護器工廠,用於創建自定義加解密類型(配置文件中可以通過alg:algorithm_name方法使用自定義的加密演算法)。
  ● Purpose(內部類型):用於根據加密目的來生成真正用於加密和校驗的密鑰,Identity使用的目的為User_MacineKey_Protect,User_MacineKey_Protect的主目的為User.MachineKey.Protect,特殊目的為"Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware", "ApplicationCookie","v1"(數據來自源碼分析)。換句話說如果密鑰相同,但是加密目的不一樣,那麼真實用於加解密的密鑰也是不同的。

  

  上圖為Purpose的定義,從定義中也可以看出針對功能的不同如Forms驗證的角色信息的以及WebForm中一系列組件的目的均不相同。

    ● NetFXCryptoService(內部類型):MachineKey在.Net平臺下使用的加解密服務組件。也是Identity中使用的身份信息加解密組件。

   以下代碼為NetFXCryptoService加解密的演算法,其演算法包括了數據加解密以及數據完整性校驗兩個部分:

  加密:

 1 public byte[] Protect(byte[] clearData) //claerData為需要加密的二進位數據
 2     {
 3         byte[] buffer4;
 4         using (SymmetricAlgorithm algorithm = this._cryptoAlgorithmFactory.GetEncryptionAlgorithm()) //通過工廠獲取加密演算法,實際上就是使用預設的或配置文件指定的如AES等
 5         {
 6             algorithm.Key = this._encryptionKey.GetKeyMaterial();//Purpose通過配置文件獲取加密密鑰並根據實際目的派生出來的真實密鑰
 7             if (this._predictableIV)
 8             {
 9                 algorithm.IV = CryptoUtil.CreatePredictableIV(clearData, algorithm.BlockSize);
10             }
11             else
12             {
13                 algorithm.GenerateIV();
14             }
15             byte[] iV = algorithm.IV;
16             using (MemoryStream stream = new MemoryStream())
17             {
18                 stream.Write(iV, 0, iV.Length);
19                 using (ICryptoTransform transform = algorithm.CreateEncryptor())
20                 {
21                     using (CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write))
22                     {
23                         stream2.Write(clearData, 0, clearData.Length);
24                         stream2.FlushFinalBlock();
25                         using (KeyedHashAlgorithm algorithm2 = this._cryptoAlgorithmFactory.GetValidationAlgorithm())//通過工廠獲取數據校驗的演算法,該演算法在配置文件中配置,如SHA1等
26                         {
27                             algorithm2.Key = this._validationKey.GetKeyMaterial();//Purpose通過配置文件獲取的數據校驗密鑰並根據實際目的派生出來的真實密鑰
28                             byte[] buffer = algorithm2.ComputeHash(stream.GetBuffer(), 0, (int) stream.Length);
29                             stream.Write(buffer, 0, buffer.Length);
30                             buffer4 = stream.ToArray();
31                         }
32                     }
33                 }
34             }
35         }
36         return buffer4;
37     }
View Code

  解密(加密的反過程):

 1 public byte[] Unprotect(byte[] protectedData)
 2     {
 3         byte[] buffer3;
 4         using (SymmetricAlgorithm algorithm = this._cryptoAlgorithmFactory.GetEncryptionAlgorithm())
 5         {
 6             algorithm.Key = this._encryptionKey.GetKeyMaterial();
 7             using (KeyedHashAlgorithm algorithm2 = this._cryptoAlgorithmFactory.GetValidationAlgorithm())
 8             {
 9                 algorithm2.Key = this._validationKey.GetKeyMaterial();
10                 int offset = algorithm.BlockSize / 8;
11                 int num2 = algorithm2.HashSize / 8;
12                 int count = (protectedData.Length - offset) - num2;
13                 if (count <= 0)
14                 {
15                     return null;
16                 }
17                 byte[] buffer = algorithm2.ComputeHash(protectedData, 0, offset + count);
18                 if (!CryptoUtil.BuffersAreEqual(protectedData, offset + count, num2, buffer, 0, buffer.Length))
19                 {
20                     buffer3 = null;
21                 }
22                 else
23                 {
24                     byte[] dst = new byte[offset];
25                     Buffer.BlockCopy(protectedData, 0, dst, 0, dst.Length);
26                     algorithm.IV = dst;
27                     using (MemoryStream stream = new MemoryStream())
28                     {
29                         using (ICryptoTransform transform = algorithm.CreateDecryptor())
30                         {
31                             using (CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write))
32                             {
33                                 stream2.Write(protectedData, offset, count);
34                                 stream2.FlushFinalBlock();
35                                 buffer3 = stream.ToArray();
36                             }
37                         }
38                     }
39                 }
40             }
41         }
42         return buffer3;
43     }
View Code

自定義Identity身份信息的驗證(基於MachineKey)

  本例將在Controller的Action方法中獲取登錄生成的Cookie值,並將其解密後反序列化成AuthenticactionTicket實例:

  代碼:

 1  public ActionResult Index()
 2         {
 3             //1.從Cookie中獲取加密後的用戶信息字元串
 4             var cookieStr = this.HttpContext.Request.Cookies[".AspNet.ApplicationCookie"].Value.ToString();
 5             //2.將用戶信息字元串以Base64Url的方式轉換為二進位數據
 6             var cookieBytes = TextEncodings.Base64Url.Decode(cookieStr);
 7             //3.轉換後的二進位數據通過MachineKey進行解密(註:MachinKey預設使用User_MacineKey_Protect為主目的,
 8             //特殊目的由Owin Cookie驗證中間件提供)
 9             var result = MachineKey.Unprotect(cookieBytes,
10                 new string[] { "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware",
11                 "ApplicationCookie",
12                 "v1"});
13             TicketSerializer ticketSerializer = new TicketSerializer();
14             //4.將解密後的二進位數據反序列化為AuthenticationTicket實例
15             var ticket = ticketSerializer.Deserialize(result);
16             
17             return View();
18         }
View Code

  登錄後的運行結果:

  

  註:MachineKey可以通過配置文件來改變加解密以及數據驗證的演算法及密鑰,該配置文件可以通過IIS的“電腦密鑰”功能來實現:

  

  

小結

  本章在軟體開發中常用的加密演算法及其在.Net中的應用介紹的基礎上,引出了Identity中用戶密碼以及用戶信息的加解密的過程與方法,其中用戶密碼的加解密較為簡單,而用戶信息作為一個複雜的對象實例,在加解密之前還需要進行序列化與反序列的流程,另外也得知了對於用戶信息的保護不僅僅是加密而且還附帶了數據完整性驗證功能。數據安全是一個非常重要的話題,而Identity的身份驗證是預設ASP.NET MVC帶有獨立身份驗證模板提供的功能,一個一分鐘就能創建的應用程式模板就提供瞭如此複雜的用戶數據安全保護功能,由此可見.Net的強大之處。

  另外本章除了介紹Identity,實際上也是介紹了一種數據保護以及身份驗證的方式,在沒有使用Identity的情況下,仍舊可以使用其理念來打造一個符合自身需求的數據保護方案。

參考:

  https://docs.microsoft.com/en-us/dotnet/standard/security/cryptography-model
  https://msdn.microsoft.com/en-us/library/ff648652.aspx
  https://www.rfc-editor.org/rfc/rfc2898.txt
  https://www.codeproject.com/articles/16645/asp-net-machinekey-generator
  http://www.cnblogs.com/happyhippy/archive/2006/12/23/601353.html
  http://mp.weixin.qq.com/s?__biz=MzIwMzg1ODcwMw==&mid=2247486407&amp;idx=1&amp;sn=51dfbce7d04ab6faeb0f5a27a5bdcbf8&source=41#wechat_redirect

本文鏈接:http://www.cnblogs.com/selimsong/p/7771875.html 

 ASP.NET沒有魔法——目錄


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、幾種枚舉類代碼示例 1、最簡單枚舉類 2、一般枚舉類用法 二、枚舉類方法 1、values()方法,返回一個enum實例的數組,數組中元素嚴格保持其在enum中聲明時的順序 2、ordinal(),返回該enum實例在enum中聲明的次序(從0開始) 3、compareTo()方法,比較enum ...
  • 什麼是框架 ...
  • 1、整數轉二進位 2、搖骰子游戲 3、猜密碼 4、查詢天氣 首先創建city.py ...
  • 人生得意須盡歡,莫使金樽空對月。 先天下之憂而憂,後天下之樂而樂。 大東北的天氣已經漸入佳境了,在夜深人靜的時候,隨著滑鼠的移動,鍵盤清脆的聲音,開啟了今天的睡前代碼工程!今天聊聊JDBC版本的分頁,分頁功能在很多的web項目中都是必須的功能,然而分頁有真假分頁,假分頁,例如某某某網站,你懂得。什麼 ...
  • Spring簡介 網站: 複雜的Java EE項目用Spring才會得到優化,如果太簡單的項目用框架反而會變的麻煩。 ...
  • 背景 最近興趣使然寫了幾個Python庫,也發佈到了Pypi上,雖然沒什麼人下載,但自己在其他機器上用著也會很方便。這裡我向大家介紹一下如何在Pypi上發表自己的Python庫。 準備 註冊賬號 很顯然地要在Pypi上註冊一個賬號。 安裝必要的庫 setuptools 原則上安裝了pip的環境都有s ...
  • 初學者,寫的不好請指出。 #第一步以insertTime為條件查詢時間段內的數據 #第二部步可以選擇是否再以通話Id為條件篩選第一步所查詢出來的數據 #因為使用的是配置文件,所以首先在代碼當前目錄下創建一個配置文件,db.conf 代碼: ...
  • 面向對象編程的基本理念與核心設計思想 解釋下多態性(polymorphism),封裝性(encapsulation),內聚(cohesion)以及耦合(coupling)。 繼承(Inheritance)與聚合(Aggregation)的區別在哪裡。 你是如何理解乾凈的代碼(Clean Code)與 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...