Web API系列(二)接口安全和參數校驗

  之前簡單介紹過web api 的設計,可是仍是有不少朋友問我,如何合理的設計和實現web api。好比,接口安全,異常處理,統一數據返回等問題。因此有必要系統的總結總結 web api 的設計和實現。因爲前面已經介紹過web api 的參數和返回格式的設計,《Web API系列(一)設計經驗與總結》此次,就來說講接口安全。html

 

  因爲Web API是基於互聯網的應用,所以安全性要遠比在本地訪問數據庫的要嚴格的多,通常通用的作法,是採用幾步來保證接口和數據安全:web

  1.首先一個是基於CA證書的HTTPS進行數據傳輸,防止數據被竊聽;數據庫

  2.而後是採用參數加密簽名方式傳遞,對傳遞的參數,增長一個加密簽名,在服務器端驗證簽名內容,防止被篡改;api

  3.最後是對通常的接口訪問,都須要使用用戶身份的token進行校驗,只要檢查經過才容許訪問數據。安全

 

  Web API接口的訪問方式,大概能夠分爲幾類:服務器

  1)使用用戶名密碼。這種方式比較簡單,能夠有效識別用戶的身份(如包括用戶信息、密碼、或者相關的接口權限等等)。驗證成功後,返回相關的數據。微信

  2)使用安全簽名。這種方式提交的數據,URL鏈接的簽名參數是通過安全必定規則的加密的,服務器收到數據後也通過一樣規則的安全加密,確認數據沒有被中途篡改後,再進行數據修改處理。所以咱們能夠爲不一樣客戶端,如Web/APP/Winfrom等不一樣接入方式指定不一樣的加密祕鑰,可是祕鑰是雙方約定的,並不在網絡鏈接上傳輸,鏈接傳輸的通常是這個接入的AppID,服務器經過這個AppID來進行簽名參數的加密對比。目前微信後臺的回調處理機制,應該就是這麼處理的。網絡

  3)公開的接口調用,不須要傳入用戶令牌、或者對參數進行加密簽名的,這種接口通常較少,只是提供一些很常規的數據顯示而已。app

   

 

  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;
        }
    }

  代碼供你們參考和學習,正式的項目能夠根據本身公司的須要去設計,後續也會開源相關的完整項目源代碼。

相關文章
相關標籤/搜索