zoukankan      html  css  js  c++  java
  • webapi接口安全验证

    其实跟大多数网上的方法一样,在前端请求头里加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

    纸上得来终觉浅,绝知此事要躬行
  • 相关阅读:
    【转】Python 可视化神器-Plotly Express
    【转】5个常用的深度学习框架
    [转]numpy中的np.max 与 np.maximum区别
    Win10环境Tensorflow-GPU13.1/JupyterNotebook的安装
    [How to] 使用HBase协处理器---基本概念和regionObserver的简单实现
    [会装]Spark standalone 模式的安装
    Hadoop-MR[会用]MR程序的运行模式
    [hadoop][基本原理]zookeeper场景使用
    [hadoop][会装]HBase集群安装--基于hadoop ha模式
    [hadoop][基本原理]zookeeper简单使用
  • 原文地址:https://www.cnblogs.com/zhaoshang/p/9371684.html
Copyright © 2011-2022 走看看