以前簡單介紹過web api 的設計,但是還是有很多朋友問我,如何合理的設計和實現web api。比如,介面安全,異常處理,統一數據返回等問題。所以有必要系統的總結總結 web api 的設計和實現。由於前面已經介紹過web api 的參數和返回格式的設計,《Web API系列(一)設計經驗與總結》 ...
以前簡單介紹過web api 的設計,但是還是有很多朋友問我,如何合理的設計和實現web api。比如,介面安全,異常處理,統一數據返回等問題。所以有必要系統的總結總結 web api 的設計和實現。由於前面已經介紹過web api 的參數和返回格式的設計,《Web API系列(一)設計經驗與總結》。這次,就來講講介面安全。
由於Web API是基於互聯網的應用,因此安全性要遠比在本地訪問資料庫的要嚴格的多,一般通用的做法,是採用幾步來保證介面和數據安全:
1.首先一個是基於CA證書的HTTPS進行數據傳輸,防止數據被竊聽;
2.然後是採用參數加密簽名方式傳遞,對傳遞的參數,增加一個加密簽名,在伺服器端驗證簽名內容,防止被篡改;
3.最後是對一般的介面訪問,都需要使用用戶身份的token進行校驗,只要檢查通過才允許訪問數據。
Web API介面的訪問方式,大概可以分為幾類:
1)使用用戶名密碼。這種方式比較簡單,可以有效識別用戶的身份(如包括用戶信息、密碼、或者相關的介面許可權等等)。驗證成功後,返回相關的數據。
2)使用安全簽名。這種方式提交的數據,URL連接的簽名參數是經過安全一定規則的加密的,伺服器收到數據後也經過同樣規則的安全加密,確認數據沒有被中途篡改後,再進行數據修改處理。因此我們可以為不同客戶端,如Web/APP/Winfrom等不同接入方式指定不同的加密秘鑰,但是秘鑰是雙方約定的,並不在網路連接上傳輸,連接傳輸的一般是這個接入的AppID,伺服器通過這個AppID來進行簽名參數的加密對比。目前微信後臺的回調處理機制,應該就是這麼處理的。
3)公開的介面調用,不需要傳入用戶令牌、或者對參數進行加密簽名的,這種介面一般較少,只是提供一些很常規的數據顯示而已。
web api 安全校驗
使用用戶名密碼的實現方式比較簡單,這裡就不說明如何實現了。就講一講安全簽名的實現。由於Web API的調用,都是一種無狀態的調用方式,所有的介面請求,都要帶安全簽名。
web api核心安全校驗代碼片斷:
public class QueryData { public QueryData() { } public QueryData(IEnumerable<KeyValuePair<string, string>> paramList) { // TODO: Complete member initialization try { if (paramList == null) { throw new Exception("請求參數為空!"); } foreach (var param in paramList) { m_values[param.Key] = param.Value; // } } catch (Exception ex) { throw new Exception(ex.Message); } } //採用排序的Dictionary的好處是方便對數據包進行簽名,不用再簽名之前再做一次排序 private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>(); /** * 設置某個欄位的值 * @param key 欄位名 * @param value 欄位值 */ public void SetValue(string key, object value) { m_values[key] = value; } /** * 根據欄位名獲取某個欄位的值 * @param key 欄位名 * @return key對應的欄位值 */ public object GetValue(string key) { object o = null; m_values.TryGetValue(key, out o); return o; } /** * 判斷某個欄位是否已設置 * @param key 欄位名 * @return 若欄位key已被設置,則返回true,否則返回false */ public bool IsSet(string key) { object o = null; m_values.TryGetValue(key, out o); if (null != o) return true; else return false; } public string ToUrl() { string buff = ""; foreach (KeyValuePair<string, object> pair in m_values) { if (pair.Value == null) { throw new Exception("內部含有值為null的欄位!"); } if (pair.Key != "sign" && pair.Value.ToString() != "") { buff += pair.Key + "=" + pair.Value + "&"; } } buff = buff.Trim('&'); return buff; } public string MakeSign(string appKey = "test") { //轉url格式 string str = ToUrl(); //在string後加入API KEY str += "&key=" + appKey; //MD5加密 var md5 = MD5.Create(); var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str)); var sb = new StringBuilder(); foreach (byte b in bs) { sb.Append(b.ToString("x2")); } //所有字元轉為大寫 return sb.ToString().ToUpper(); } public bool CheckSign() { //如果沒有設置簽名,則跳過檢測 if (!IsSet("sign")) { throw new Exception("簽名存在但不合法!"); } //如果設置了簽名但是簽名為空,則拋異常 else if (GetValue("sign") == null || GetValue("sign").ToString() == "") { throw new Exception("簽名存在但不合法!"); } //獲取接收到的簽名 string return_sign = GetValue("sign").ToString(); //在本地計算新的簽名 string cal_sign = MakeSign(); if (cal_sign == return_sign) { return true; } return false; } }
代碼供大家參考和學習,正式的項目可以根據自己公司的需要去設計,後續也會開源相關的完整項目源代碼。
參考頁面:http://qingqingquege.cnblogs.com/p/5933752.html