zoukankan      html  css  js  c++  java
  • Web API系列(二)接口安全和参数校验

      以前简单介绍过web api 的设计,但是还是有很多朋友问我,如何合理的设计和实现web api。比如,接口安全,异常处理,统一数据返回等问题。所以有必要系统的总结总结 web api 的设计和实现。由于前面已经介绍过web api 的参数和返回格式的设计,《Web API系列(一)设计经验与总结》这次,就来讲讲接口安全。

      由于Web API是基于互联网的应用,因此安全性要远比在本地访问数据库的要严格的多,一般通用的做法,是采用几步来保证接口和数据安全:

      1.首先一个是基于CA证书的HTTPS进行数据传输,防止数据被窃听;

      2.然后是采用参数加密签名方式传递,对传递的参数,增加一个加密签名,在服务器端验证签名内容,防止被篡改;

      3.最后是对一般的接口访问,都需要使用用户身份的token进行校验,只要检查通过才允许访问数据。

      Web API接口的访问方式,大概可以分为几类:

      1)使用用户名密码。这种方式比较简单,可以有效识别用户的身份(如包括用户信息、密码、或者相关的接口权限等等)。验证成功后,返回相关的数据。

      2)使用安全签名。这种方式提交的数据,URL连接的签名参数是经过安全一定规则的加密的,服务器收到数据后也经过同样规则的安全加密,确认数据没有被中途篡改后,再进行数据修改处理。因此我们可以为不同客户端,如Web/APP/Winfrom等不同接入方式指定不同的加密秘钥,但是秘钥是双方约定的,并不在网络连接上传输,连接传输的一般是这个接入的AppID,服务器通过这个AppID来进行签名参数的加密对比。目前微信后台的回调处理机制,应该就是这么处理的。

      3)公开的接口调用,不需要传入用户令牌、或者对参数进行加密签名的,这种接口一般较少,只是提供一些很常规的数据显示而已。

       

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

      代码供大家参考和学习,正式的项目可以根据自己公司的需要去设计,后续也会开源相关的完整项目源代码。

  • 相关阅读:
    vue-fullcalendar插件
    iframe 父框架调用子框架的函数
    关于调试的一点感想
    hdfs 删除和新增节点
    hadoop yarn 实战错误汇总
    Ganglia 安装 No package 'ck' found
    storm on yarn(CDH5) 部署笔记
    spark on yarn 安装笔记
    storm on yarn安装时 提交到yarn失败 failed
    yarn storm spark
  • 原文地址:https://www.cnblogs.com/zhangweizhong/p/6196808.html
Copyright © 2011-2022 走看看