webapi接口安全驗證

其實跟大多數網上的方法同樣,在前端請求頭裏加token,後臺經過攔截器處理token數據,而後兩邊對比,若是同樣就能經過,不同就返回無權限。前端

前端測試代碼以下:ajax

@{
    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>
    }

好吧,先不吐槽個人加密字符串的加入方式。。。json

如上,咱們須要的是將參數加入到一個集合,而後排序,而後拼接成一個字符串,而後轉碼,而後MD5加密。裏面的時間戳是表示這個sign是有時限的。後端

而後是攔截器的代碼:api

        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),只要你傳了參數,而後我須要保證先後端的參數是一致的。這裏就作了幾個限制。app

1.請求是Get是沒問題,其餘請求只支持 multipart/form-data,application/json,application/x-www-form-urlencoded,text/plain; charset=UTF-8,目前這些對我來講是夠用了。而後每種方式傳參方式咯有不一樣,先後端統一就能夠了。async

2.當有文件時,文件字段是不加入到加密字符中的ide

而後在調試過程當中遇到過一個問題。在multipart/form-data狀況下,actionContext.Request.Content.ReadAsMultipartAsync().Result.Contents會發生死鎖,而後前端就一直等啊等,解決方法如上。post

另外還有其餘辦法 https://stackoverflow.com/questions/15201255/request-content-readasmultipartasync-never-returns測試

相關文章
相關標籤/搜索