日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

WebApi安全性 使用TOKEN+签名验证

發布時間:2024/9/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WebApi安全性 使用TOKEN+签名验证 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先問大家一個問題,你在寫開放的API接口時是如何保證數據的安全性的?先來看看有哪些安全性問題在開放的api接口中,我們通過http Post或者Get方式請求服務器的時候,會面臨著許多的安全性問題,例如:

  • 請求來源(身份)是否合法?
  • 請求參數被篡改?
  • 請求的唯一性(不可復制),防止請求被惡意攻擊
  • 為了保證數據在通信時的安全性,我們可以采用TOKEN+參數簽名的方式來進行相關驗證。

    比如說我們客戶端需要查詢產品信息這個操作來進行分析,客戶端點擊查詢按鈕==》調用服務器端api進行查詢==》服務器端返回查詢結果

    一、不進行驗證的方式

    api查詢接口:

    客戶端調用:http://api.XXX.com/getproduct?id=value1

    如上,這種方式簡單粗暴,在瀏覽器直接輸入"http://api.XXX.com/getproduct?id=value1",即可獲取產品列表信息了,但是這樣的方式會存在很嚴重的安全性問題,沒有進行任何的驗證,大家都可以通過這個方法獲取到產品列表,導致產品信息泄露。
    那么,如何驗證調用者身份呢?如何防止參數被篡改呢?如何保證請求的唯一性? 如何保證請求的唯一性,防止請求被惡意攻擊呢?

    二、使用TOKEN+簽名認證 保證請求安全性

    token+簽名認證的主要原理是:1.做一個認證服務,提供一個認證的webapi,用戶先訪問它獲取對應的token

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?2.用戶拿著相應的token以及請求的參數和服務器端提供的簽名算法計算出簽名后再去訪問指定的api

                   ?3.服務器端每次接收到請求就獲取對應用戶的token和請求參數,服務器端再次計算簽名和客戶端簽名做對比,如果驗證通過則正常訪問相應的api,驗證失敗則返回具體的失敗信息

    具體代碼如下 :

    1.用戶請求認證服務GetToken,將TOKEN保存在服務器端緩存中,并返回對應的TOKEN到客戶端(該請求不需要進行簽名認證)

    public HttpResponseMessage GetToken(string staffId){ResultMsg resultMsg = null;int id = 0;//判斷參數是否合法if (string.IsNullOrEmpty(staffId) || (!int.TryParse(staffId, out id))){resultMsg = new ResultMsg();resultMsg.StatusCode = (int)StatusCodeEnum.ParameterError;resultMsg.Info = StatusCodeEnum.ParameterError.GetEnumText();resultMsg.Data = "";return HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg));}//插入緩存Token token =(Token)HttpRuntime.Cache.Get(id.ToString());if (HttpRuntime.Cache.Get(id.ToString()) == null){token = new Token();token.StaffId = id;token.SignToken = Guid.NewGuid();token.ExpireTime = DateTime.Now.AddDays(1);HttpRuntime.Cache.Insert(token.StaffId.ToString(), token, null, token.ExpireTime, TimeSpan.Zero);}//返回token信息resultMsg =new ResultMsg();resultMsg.StatusCode = (int)StatusCodeEnum.Success;resultMsg.Info = "";resultMsg.Data = token;return HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg));}

    2.客戶端調用服務器端API,需要對請求進行簽名認證,簽名方式如下?

    (1) get請求:按照請求參數名稱將所有請求參數按照字母先后順序排序得到:keyvaluekeyvalue...keyvalue??字符串如:將arong=1,mrong=2,crong=3?排序為:arong=1,?crong=3,mrong=2??然后將參數名和參數值進行拼接得到參數字符串:arong1crong3mrong2。? 

    public static Tuple<string,string> GetQueryString(Dictionary<string, string> parames){// 第一步:把字典按Key的字母順序排序IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parames);IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();// 第二步:把所有參數名和參數值串在一起StringBuilder query = new StringBuilder(""); //簽名字符串StringBuilder queryStr = new StringBuilder(""); //url參數if (parames == null || parames.Count == 0)return new Tuple<string,string>("","");while (dem.MoveNext()){string key = dem.Current.Key;string value = dem.Current.Value;if (!string.IsNullOrEmpty(key)){query.Append(key).Append(value);queryStr.Append("&").Append(key).Append("=").Append(value);}}return new Tuple<string, string>(query.ToString(), queryStr.ToString().Substring(1, queryStr.Length - 1));}

    post請求:將請求的參數對象序列化為json格式字符串   

    Product product = new Product() { Id = 1, Name = "安慕希", Count = 10, Price = 58.8 };var data=JsonConvert.SerializeObject(product);

    (2)在請求頭中添加timespan(時間戳),nonce(隨機數),staffId(用戶Id),signature(簽名參數)   

    //加入頭信息request.Headers.Add("staffid", staffId.ToString()); //當前請求用戶StaffIdrequest.Headers.Add("timestamp", timeStamp); //發起請求時的時間戳(單位:毫秒)request.Headers.Add("nonce", nonce); //發起請求時的時間戳(單位:毫秒)request.Headers.Add("signature", GetSignature(timeStamp,nonce,staffId,data)); //當前請求內容的數字簽名

    (3)根據請求參數計算本次請求的簽名,用timespan+nonc+staffId+token+data(請求參數字符串)得到signStr簽名字符串,然后再進行排序和MD5加密得到最終的signature簽名字符串,添加到請求頭中

    private static string GetSignature(string timeStamp,string nonce,int staffId,string data){Token token = null;var resultMsg = GetSignToken(staffId);if (resultMsg != null){if (resultMsg.StatusCode == (int)StatusCodeEnum.Success){token = resultMsg.Result;}else{throw new Exception(resultMsg.Data.ToString());}}else{throw new Exception("token為null,員工編號為:" +staffId);}var hash = System.Security.Cryptography.MD5.Create();//拼接簽名數據var signStr = timeStamp +nonce+ staffId + token.SignToken.ToString() + data;//將字符串中字符按升序排序var sortStr = string.Concat(signStr.OrderBy(c => c));var bytes = Encoding.UTF8.GetBytes(sortStr);//使用MD5加密var md5Val = hash.ComputeHash(bytes);//把二進制轉化為大寫的十六進制StringBuilder result = new StringBuilder();foreach (var c in md5Val){result.Append(c.ToString("X2"));}return result.ToString().ToUpper();}

    (4) webapi接收到相應的請求,取出請求頭中的timespan,nonc,staffid,signature 數據,根據timespan判斷此次請求是否失效,根據staffid取出相應token判斷token是否失效,根據請求類型取出對應的請求參數,然后服務器端按照同樣的規則重新計算請求簽名,判斷和請求頭中的signature數據是否相同,如果相同的話則是合法請求,正常返回數據,如果不相同的話,該請求可能被惡意篡改,禁止訪問相應的數據,返回相應的錯誤信息?

    ?如下使用全局過濾器攔截所有api請求進行統一的處理

    public class ApiSecurityFilter : ActionFilterAttribute{public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext){ResultMsg resultMsg = null;var request = actionContext.Request;string method = request.Method.Method;string staffid = String.Empty, timestamp = string.Empty, nonce = string.Empty, signature = string.Empty;int id = 0;if (request.Headers.Contains("staffid")){staffid = HttpUtility.UrlDecode(request.Headers.GetValues("staffid").FirstOrDefault());}if (request.Headers.Contains("timestamp")){timestamp = HttpUtility.UrlDecode(request.Headers.GetValues("timestamp").FirstOrDefault());}if (request.Headers.Contains("nonce")){nonce = HttpUtility.UrlDecode(request.Headers.GetValues("nonce").FirstOrDefault());}if (request.Headers.Contains("signature")){signature = HttpUtility.UrlDecode(request.Headers.GetValues("signature").FirstOrDefault());}//GetToken方法不需要進行簽名驗證if (actionContext.ActionDescriptor.ActionName == "GetToken"){if (string.IsNullOrEmpty(staffid) || (!int.TryParse(staffid, out id) || string.IsNullOrEmpty(timestamp) || string.IsNullOrEmpty(nonce))){resultMsg = new ResultMsg();resultMsg.StatusCode = (int)StatusCodeEnum.ParameterError;resultMsg.Info = StatusCodeEnum.ParameterError.GetEnumText();resultMsg.Data = "";actionContext.Response = HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg));base.OnActionExecuting(actionContext);return;}else{base.OnActionExecuting(actionContext);return;}}//判斷請求頭是否包含以下參數if (string.IsNullOrEmpty(staffid) || (!int.TryParse(staffid, out id) || string.IsNullOrEmpty(timestamp) || string.IsNullOrEmpty(nonce) || string.IsNullOrEmpty(signature))){resultMsg = new ResultMsg();resultMsg.StatusCode = (int)StatusCodeEnum.ParameterError;resultMsg.Info = StatusCodeEnum.ParameterError.GetEnumText();resultMsg.Data = "";actionContext.Response = HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg));base.OnActionExecuting(actionContext);return;}//判斷timespan是否有效double ts1 = 0;double ts2 = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds;bool timespanvalidate = double.TryParse(timestamp, out ts1);double ts = ts2 - ts1;bool falg = ts > int.Parse(WebSettingsConfig.UrlExpireTime) * 1000;if (falg || (!timespanvalidate)){resultMsg = new ResultMsg();resultMsg.StatusCode = (int)StatusCodeEnum.URLExpireError;resultMsg.Info = StatusCodeEnum.URLExpireError.GetEnumText();resultMsg.Data = "";actionContext.Response = HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg));base.OnActionExecuting(actionContext);return;}//判斷token是否有效Token token = (Token)HttpRuntime.Cache.Get(id.ToString());string signtoken = string.Empty;if (HttpRuntime.Cache.Get(id.ToString()) == null){resultMsg = new ResultMsg();resultMsg.StatusCode = (int)StatusCodeEnum.TokenInvalid;resultMsg.Info = StatusCodeEnum.TokenInvalid.GetEnumText();resultMsg.Data = "";actionContext.Response = HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg));base.OnActionExecuting(actionContext);return;}else{signtoken = token.SignToken.ToString();}//根據請求類型拼接參數NameValueCollection form = HttpContext.Current.Request.QueryString;string data = string.Empty;switch (method){case "POST":Stream stream = HttpContext.Current.Request.InputStream;string responseJson = string.Empty;StreamReader streamReader = new StreamReader(stream);data = streamReader.ReadToEnd();break;case "GET"://第一步:取出所有get參數IDictionary<string, string> parameters = new Dictionary<string, string>();for (int f = 0; f < form.Count; f++){string key = form.Keys[f];parameters.Add(key, form[key]);}// 第二步:把字典按Key的字母順序排序IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();// 第三步:把所有參數名和參數值串在一起StringBuilder query = new StringBuilder();while (dem.MoveNext()){string key = dem.Current.Key;string value = dem.Current.Value;if (!string.IsNullOrEmpty(key)){query.Append(key).Append(value);}}data = query.ToString();break;default:resultMsg = new ResultMsg();resultMsg.StatusCode = (int)StatusCodeEnum.HttpMehtodError;resultMsg.Info = StatusCodeEnum.HttpMehtodError.GetEnumText();resultMsg.Data = "";actionContext.Response = HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg));base.OnActionExecuting(actionContext);return;}bool result = SignExtension.Validate(timestamp, nonce, id, signtoken,data, signature);if (!result){resultMsg = new ResultMsg();resultMsg.StatusCode = (int)StatusCodeEnum.HttpRequestError;resultMsg.Info = StatusCodeEnum.HttpRequestError.GetEnumText();resultMsg.Data = "";actionContext.Response = HttpResponseExtension.toJson(JsonConvert.SerializeObject(resultMsg));base.OnActionExecuting(actionContext);return;}else{base.OnActionExecuting(actionContext);}}public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext){base.OnActionExecuted(actionExecutedContext);}}

    然后我們進行測試,檢驗api請求的合法性

    Get請求:

    1.獲取產品數據,傳遞參數id=1,name="wahaha" ?,完整請求為http://localhost:14826/api/product/getproduct?id=1&name=wahaha

    2.請求頭添加timespan,staffid,nonce,signature字段

    ?3.如圖當data里面的值為id1namewahaha的時候請求頭中的signature和服務器端計算出來的result的值是完全一樣的,當我將data修改為id1namewahaha1之后,服務器端計算出來的簽名result和請求頭中提交的signature就不相同了,就表示為不合法的請求了

    4.不合法的請求就會被識別為請求參數已被修改

    ? 合法的請求則會返回對應的商品信息

    post請求:

    1.post對象序列化為json字符串后提交到后臺,后臺返回相應產品信息

    2.后臺獲取請求的參數信息

    3.判斷簽名是否成功,第一次請求簽名參數signature和服務器端計算result完全相同, 然后當把請求參數中count的數量從10改成100之后服務器端計算的result和請求簽名參數signature不同,所以請求不合法,是非法請求,同理如果其他任何參數被修改最后計算的結果都會和簽名參數不同,請求同樣識別為不合法請求

    總結:

    通過上面的案例,我們可以看出,安全的關鍵在于參與簽名的TOKEN,整個過程中TOKEN是不參與通信的,所以只要保證TOKEN不泄露,請求就不會被偽造。

    然后我們通過timestamp時間戳用來驗證請求是否過期,這樣就算被人拿走完整的請求鏈接也是無效的。

    Sign簽名的方式能夠在一定程度上防止信息被篡改和偽造,保障通信的安全

    源碼地址:https://github.com/13138899620/TokenSign

    總結

    以上是生活随笔為你收集整理的WebApi安全性 使用TOKEN+签名验证的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 亚洲av综合色区无码二区爱av | 久久免费视频精品 | 青青操网站 | 成人免费毛片高清视频 | 男人舔女人下部高潮全视频 | 欧美理伦少妇2做爰 | 男女午夜视频在线观看 | 色播五月综合 | 成人黄色片免费 | 亚洲无码一区二区三区 | 人妻丰满熟妇av无码区hd | 亚洲乱码国产乱码精品精大量 | 99热日韩 | 国产精品99久久久久久www | 国产一级片麻豆 | 修女也疯狂3免费观看完整版 | babes性欧美69 | 久久久欧美精品sm网站 | 国产对白videos麻豆高潮 | 少妇精品无码一区二区免费视频 | 最新福利在线 | 青青青在线视频免费观看 | 中文字幕日韩在线播放 | 久草福利在线视频 | 欧美在线视频一区二区三区 | 红桃成人在线 | 欧美日韩免费做爰视频 | 亚洲天堂一 | 蜜桃视频在线观看一区二区 | 亚洲一区二区精品在线 | 国产区精品在线 | 绯色av一区二区 | www.96av| 国产一级久久 | 在线中文字幕播放 | 韩产日产国产欧产 | 国产成人在线播放视频 | 国产小精品 | 手机看片日韩久久 | 国产精品va| www操| 一级黄色片网站 | 亚洲综合久久网 | 2020自拍偷拍 | 色先锋av | 日本不卡一区在线 | 日日摸夜夜添狠狠添久久精品成人 | 亚洲女人天堂 | 男人的天堂久久 | 亚洲男人天堂2020 | 日韩激情一区二区 | 欧洲熟妇精品视频 | 日韩中文字幕精品视频 | 久久久久久蜜桃一区二区 | h官场少妇第三部分 | 国产sm调教一区二区 | 国产一区二区免费视频 | 日韩一区二区三区三四区视频在线观看 | 波多野结衣家庭主妇 | 国产精品乱码久久久久久久久 | 亚洲综合激情另类小说区 | 欧美一区二区免费在线观看 | 亚洲天堂社区 | 国产人妻人伦精品1国产丝袜 | 中文字幕av无码一区二区三区 | 亚洲第九页 | 国精产品一二三区精华液 | 日韩成人av网址 | 国产96视频 | 国产一级二级毛片 | 欧美视频xxxx | 草草草在线| 天堂中文字幕在线 | 欧美精品一区二区三区久久久竹菊 | 韩国美女黄色片 | 亚洲av无码一区二区乱孑伦as | 亚洲成人中文 | 天天爽天天爽夜夜爽毛片 | 最新视频在线观看 | 日韩无码精品一区二区三区 | 色资源网站 | 91美女在线视频 | 美女被草视频在线观看 | 国产精品久久成人 | 欧美日韩在线免费播放 | 好男人在线视频www 亚洲福利国产 | 亚洲高清av在线 | 免费在线你懂的 | 久久久久无码国产精品一区李宗瑞 | 欧美性猛交性大交 | 欧美图片一区二区三区 | 成年人免费在线 | 91久久综合精品国产丝袜蜜芽 | 草草色 | 精品一区久久久 | 国产一级片网站 | 男女做激情爱呻吟口述全过程 | 91系列在线观看 | 伊人99|