• 热门专题

Web API系列(二)接口安全校验

作者:章为忠  发布日期:2016-12-22 20:36:27
Tag标签:接口  
  •   以前简单介绍过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;
            }
        }

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

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规