其實跟大多數網上的方法一樣,在前端請求頭里加token,后台通過攔截器處理token數據,然后兩邊對比,如果一樣就能通過,不一樣就返回無權限。
前端測試代碼如下:
@{ ViewBag.Title = "TestApiAouth"; } <h2>測試</h2> <form id="form"> <div> <label class="form-control">PIC:</label> <input id="pic" type="file" name="pic" class="form-control" /> </div> <div> <button id="test" type="button">測試</button> </div> </form> @section scripts{ <script src="~/Scripts/md5.js"></script> <script> $(function () { $("#test").on("click", function () { var id=$("#id").val(); var name = $("#name").val(); var add_arr = new Array(); add_arr.push("method=AddSkin"); add_arr.push("appcode=PHRASE"); add_arr.push("name=一葉孤舟"); add_arr.push("type=background"); add_arr.push("font_color="); add_arr.push("price=7000"); add_arr.push("state=0"); add_arr.push("description=輕風無意助孤舟,任爾飄搖任爾游。"); add_arr.push("forever=1"); add_arr.push("sale_start="); add_arr.push("sale_end=" ); add_arr.push("timestamp=" +parseInt(new Date().getTime() / (1000 * 60*5))); add_arr.sort(); var n_sign = add_arr.join('&'); var sign = hex_md5(encodeURIComponent(n_sign).toUpperCase()); var formData = new FormData(); formData.append('pic', document.querySelector('#pic').files[0]); formData.append('appcode', "PHRASE"); formData.append('name', "一葉孤舟"); formData.append('type', "background"); formData.append('font_color', ""); formData.append('price', "7000"); formData.append('state', "0"); formData.append('description', "輕風無意助孤舟,任爾飄搖任爾游。"); formData.append('forever', "1"); formData.append('sale_start', ""); formData.append('sale_end', ""); $.ajax({ url: "http://localhost:51650/api/AppletShop/AdminShop/AddSkin", type: "post", dataType: "json", data: formData, headers: { "sign": sign }, processData: false, contentType: false, success: function (data) { if (data.success) { alert(data.message); } }, error: function (data) { alert(data); } }); }); }); </script> }
好吧,先不吐槽我的加密字符串的加入方式。。。
如上,我們需要的是將參數加入到一個集合,然后排序,然后拼接成一個字符串,然后轉碼,然后MD5加密。里面的時間戳是表示這個sign是有時限的。
然后是攔截器的代碼:
public override void OnActionExecuting(HttpActionContext actionContext) { //如果不需要檢測直接返回 if (!isCheck) return; ApiResult resultMsg = new ApiResult(); List<string> list_params = new List<string>(); string action_name = actionContext.ActionDescriptor.ActionName; list_params.Add("method=" + action_name); //參數在queryparam上 var action_params = actionContext.ActionArguments; foreach (var param in action_params) { list_params.Add(param.Key + "=" + param.Value); } var headers = actionContext.Request.Content.Headers; //如果是multipart/form-data請求 if (actionContext.Request.Content.IsMimeMultipartContent()) { System.Collections.ObjectModel.Collection<HttpContent> formdata=new System.Collections.ObjectModel.Collection<HttpContent> (); Task.Factory.StartNew(() => formdata = actionContext.Request.Content.ReadAsMultipartAsync().Result.Contents, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default).Wait(); foreach (var form in formdata) { //文件不加入到加密字符串中 if (Util.isNotNull(form.Headers.ContentDisposition.FileName)) { continue; } var key = form.Headers.ContentDisposition.Name.Replace("\"", ""); string val = form.ReadAsStringAsync().Result; list_params.Add(key + "=" + val); } } if (Util.isNotNull(headers.ContentType)) { //各種請求類型(目前只支持下面三種和上面的formdata) if (headers.ContentType.ToString() == "application/json") { var result = actionContext.Request.Content.ReadAsStringAsync().Result; list_params.Add(result); } else if (headers.ContentType.ToString() == "text/plain; charset=UTF-8") { var result = actionContext.Request.Content.ReadAsStringAsync().Result; list_params.Add(result); } else if (headers.ContentType.ToString() == "application/x-www-form-urlencoded") { var result = actionContext.Request.Content.ReadAsStringAsync().Result; var _params = result.Split('&'); list_params.AddRange(_params); } } //加時間戳 long timespan_now = Util.GetTimestamp(DateTime.Now); list_params.Add("timestamp=" + timespan_now / (1000 * 60 *5)); list_params.Sort(); //排序 //需要加密的字段 string sign_string = string.Join("&", list_params); //從header中讀取sign,timestamp.sign是方法名與方法參數的集合排序后通過&連接的字符串 string sign = SignToken.GetSignByHeader(actionContext.Request.Headers, "sign"); //string timestamp = SignToken.GetSignByHeader(actionContext.Request.Headers, "timestamp"); //判斷請求頭是否包含以下參數 if (string.IsNullOrEmpty(sign)) { resultMsg = new ApiResult { success = false, status = Util.ApiStatusCode.InvalidParam, message = "請求頭中缺少參數數據!" }; } else { //服務端加密后的參數 string server_sign = Util.MD5Encrypt(HttpUtility.UrlEncode(sign_string).ToUpper()).ToLower(); if (server_sign != sign) { resultMsg = new ApiResult { success = false, status = Util.ApiStatusCode.Unauthorized, message = "簽名或者密鑰錯誤!" }; } else { resultMsg.success = true; } } if (!resultMsg.success) { //如果驗證不通過,則返回授權錯誤,並且寫入錯誤原因 actionContext.Response = actionContext.Request.CreateResponse(resultMsg); } else { base.OnActionExecuting(actionContext); } }
我攔截器的處理方式是以獲取參數為核心,不管你是什么請求方法(get,post,delete,put),只要你傳了參數,然后我需要保證前后端的參數是一致的。這里就做了幾個限制。
1.請求是Get是沒問題,其他請求只支持 multipart/form-data,application/json,application/x-www-form-urlencoded,text/plain; charset=UTF-8,目前這些對我來說是夠用了。然后每種方式傳參方式咯有不同,前后端統一就可以了。
2.當有文件時,文件字段是不加入到加密字符中的
然后在調試過程中遇到過一個問題。在multipart/form-data情況下,actionContext.Request.Content.ReadAsMultipartAsync().Result.Contents會發生死鎖,然后前端就一直等啊等,解決方法如上。
另外還有其他辦法 https://stackoverflow.com/questions/15201255/request-content-readasmultipartasync-never-returns
