`

接口签名验证

 
阅读更多

转自:http://www.cnblogs.com/lycsmzl/p/5684514.html

 

项目中常用的API接口签名验证方法:

1. 给app分配对应的key、secret
2. Sign签名,调用API 时需要对请求参数进行签名验证,签名方式如下:
  a. 按照请求参数名称将所有请求参数按照字母先后顺序排序得到:keyvaluekeyvalue...keyvalue  字符串如:将arong=1,mrong=2,crong=3 排序为:arong=1, crong=3,mrong=2  然后将参数名和参数值进行拼接得到参数字符串:arong1crong3mrong2;
  b. 将secret加在参数字符串的头部后进行MD5加密 ,加密后的字符串需大写。即得到签名Sign;

大致处理过程

// 用户验证、判断key是否存在,同时查询出对应的secret用于验证;

....

// 验证签名,根据算法将参数进行签名得到sign与参数中的sign进行对比;

....

// 插叙数据获取列表

如下图

<iframe id="iframe_0.7531260777155655" style="margin: 0px; padding: 0px; border-width: initial; border-style: none; width: 573px; height: 279px;" src="data:text/html;charset=utf8,%3Cstyle%3Ebody%7Bmargin:0;padding:0%7D%3C/style%3E%3Cimg%20id=%22img%22%20src=%22http://img.xiumi.us/xmi/ua/xjUG/i/aebf343ebe92819d001aa6a096a191a5-sz_9747.png@1l_640w.png?_=5684514%22%20style=%22border:none;max-width:848px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.7531260777155655',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no"></iframe>

app调用:http://api.test.com/getproducts?key=app_key&sign=BCC7C71CF93F9CDBDB88671B701D8A35&参数1=value1&参数2=value2.......
注:secret 仅作加密使用, 为了保证数据安全请不要在请求参数中使用。

请求的唯一性: 为了防止别人重复使用请求参数问题,我们需要保证请求的唯一性,就是对应请求只能使用一次,这样就算别人拿走了请求的完整链接也是无效的。
唯一性的实现:在如上的请求参数中,我们加入时间戳 :timestamp(yyyyMMddHHmmss),同样,时间戳作为请求参数之一,也加入sign算法中进行加密。

下面提供2个签名验证的方法:时间戳注意加入及验证的处理

1、OpenId签名及验证

复制代码
复制代码
        /// <summary>
        /// 生成Code
        /// </summary>
        /// <param name="openid">openid</param>
        /// <param name="key">向谁跳传谁规定的key</param>
        /// <returns></returns>
        public static string MakeCode(string openid, string key)
        {
            DateTime time = DateTime.Now;
            string data = time.ToString("dd") + "_" + openid + "_" + time.ToString("yyyy") + "_" + key + "_" + time.ToString("MM");
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] result = Encoding.Default.GetBytes(data);
            byte[] output = md5.ComputeHash(result);
            data = BitConverter.ToString(output).Replace("-", "").ToLower();
            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
            byte[] bytes = sha1.ComputeHash(Encoding.ASCII.GetBytes(data));//"596d42faf5710b35c7ea0f0a9600ee31"  F69D39E1CA07FC23C1CE62F549E9D5B9780
            //转16进制 清除前面0
            StringBuilder strB = new StringBuilder();
            for (int i = 0; i < bytes.Length; i++)
            {
                string strX2 = bytes[i].ToString("X2");
                if (strX2.Substring(0, 1) == "0")
                {
                    strX2 = strX2.Substring(1, 1);
                }
                strB.Append(strX2);
            }
            return strB.ToString();
        }
        /// <summary>
        /// Code验证
        /// </summary>
        /// <param name="openid">openid</param>
        /// <param name="code">待验证的数据</param>
        /// <param name="key">自己系统规定的key</param>
        /// <returns></returns>
        public static bool ValidateCode(string openid, string code, string key)
        {
            string signedData = MakeCode(openid, key);
            return code.Equals(signedData, StringComparison.OrdinalIgnoreCase);
        }
复制代码
复制代码

2、请求参数签名及验证

复制代码
复制代码
        /// <summary>
        /// 给请求签名
        /// </summary>
        /// <param name="parameters">所有字符型的请求参数</param>
        /// <param name="secret">签名密钥</param>
        /// <param name="qhs">是否前后都加密进行签名</param>
        /// <returns></returns>
        public string SignRequest(IDictionary<string, string> parameters, string secret, bool qhs)
        {
            // 第一步:把字典按Key的字母顺序排序
            IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
            IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();
            // 第二步:把所有参数名和参数值串在一起
            StringBuilder query = new StringBuilder(secret);
            while (dem.MoveNext())
            {
                string key = dem.Current.Key;
                string value = dem.Current.Value;
                if (!string.IsNullOrWhiteSpace(key))//!string.IsNullOrWhiteSpace(value) 空值也加入计算??
                {
                    query.Append(key).Append(value);
                }
            }
            if (qhs)
            {
                query.Append(secret);
            }
            // 第三部:使用md5运算
            MD5 md5 = MD5.Create();
            byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(query.ToString()));
            // 第四部:把二进制转为大写的十六进制
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < bytes.Length; i++)
            {
                result.Append(bytes[i].ToString("X2"));
            }
            return result.ToString();
        }

        /// <summary>
        /// 验证签名
        /// </summary>
        /// <returns></returns>
        public bool ValidateSign(string secret)
        {
            string method = HttpContext.Current.Request.HttpMethod;
            System.Collections.Specialized.NameValueCollection form = HttpContext.Current.Request.QueryString;
            switch (method)
            {
                case "POST":
                    form = HttpContext.Current.Request.Form;
                    break;
                case "GET":
                    form = HttpContext.Current.Request.QueryString;
                    break;
                default:
                    return false;
            }
            IDictionary<string, string> parameters = new Dictionary<string, string>();
            string sign = string.Empty;
            for (int i = 0; i < form.Count; i++)
            {
                string key = form.Keys[i];
                if (string.Equals(key,"sign",StringComparison.OrdinalIgnoreCase))
                {
                     sign = form["sign"];
                    continue;
                }
                parameters.Add(key,form[key]);
            }
            return string.Equals(SignRequest(parameters, secret, false), sign,StringComparison.OrdinalIgnoreCase);
        }
复制代码
复制代码

3、对用户信息进行签名(通用权限管理系统最新的安全校验方法) ,实现了链接只可以使用一次

复制代码
复制代码
        /// <summary>
        /// 对登录的用户进行数字签名
        /// </summary>
        /// <param name="userLogOnResult">登录信息</param>
        /// <returns>进行过数字签名的用户登录信息</returns>
        public static bool VerifySignature(BaseUserInfo userInfo)
        {
            bool result = false;

            if (userInfo != null && !string.IsNullOrEmpty(userInfo.Signature))
            {
                if (string.IsNullOrEmpty(userInfo.Code))
                {
                    userInfo.Code = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.CompanyCode))
                {
                    userInfo.CompanyCode = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.CompanyId))
                {
                    userInfo.CompanyId = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.CompanyName))
                {
                    userInfo.CompanyName = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.DepartmentCode))
                {
                    userInfo.DepartmentCode = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.DepartmentId))
                {
                    userInfo.DepartmentId = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.DepartmentName))
                {
                    userInfo.DepartmentName = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.Id))
                {
                    userInfo.Id = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.NickName))
                {
                    userInfo.NickName = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.OpenId))
                {
                    userInfo.OpenId = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.RealName))
                {
                    userInfo.RealName = string.Empty;
                }
                if (string.IsNullOrEmpty(userInfo.UserName))
                {
                    userInfo.UserName = string.Empty;
                }
                // 需要签名的内容部分
                string dataToSign = userInfo.Code + "_"
                    + userInfo.CompanyCode + "_"
                    + userInfo.CompanyId + "_"
                    + userInfo.CompanyName + "_"
                    + userInfo.DepartmentCode + "_"
                    + userInfo.DepartmentId + "_"
                    + userInfo.DepartmentName + "_"
                    + userInfo.Id + "_"
                    + userInfo.IdentityAuthentication.ToString() + "_"
                    + userInfo.IsAdministrator.ToString() + "_"
                    + userInfo.NickName + "_"
                    + userInfo.OpenId + "_"
                    + userInfo.RealName + "_"
                    + userInfo.UserName;

                result = userInfo.Signature == DotNet.Utilities.SecretUtil.md5(dataToSign);
            }

            return result;
        }
复制代码
复制代码

在用户登录成功以后要对返回的用户信息进行一次签名

UserLogOnResult result = new UserLogOnResult();

result = userManager.LogOnByNickName(userName, password, openId, createOpenId, systemCode, ipAddress, macAddress, computerName, checkUserPassword, validateUserOnly, checkMacAddress, sourceType, targetApplication, targetIp);

 result.UserInfo = ServiceUtil.CreateSignature(result.UserInfo);

 

code的生成

复制代码
复制代码
        /// <summary>
        /// 获取登录操作的验证码
        /// code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 
        /// </summary>
        /// <param name="userInfo">用户信息</param>
        /// <returns>操作码</returns>
        public static BaseResult GetAuthorizationCode(BaseUserInfo userInfo)
        {
            BaseResult result = new BaseResult();
            
            if (ServiceUtil.VerifySignature(userInfo))
            {
                // 产生一个授权码
                string authorizationCode = Guid.NewGuid().ToString("N");
                // 设置缓存服务器,消费一次,5分钟过期。
                using (var redisClient = PooledRedisHelper.GetTokenClient())
                {
                    // 2016-03-03 吉日嘎拉 让缓存早点儿失效
                    DateTime expiresAt = DateTime.Now.AddMinutes(5);
                    string key = "code:" + authorizationCode;
                    redisClient.Set(key, userInfo.OpenId, expiresAt);
                }
                result.ResultValue = authorizationCode;
                result.Status = true;
                result.StatusCode = Status.OK.ToString();
                result.StatusMessage = Status.OK.ToDescription();
                result.CreateSignature(userInfo);
            }

            return result;
        }
复制代码
复制代码

code的验证

复制代码
复制代码
        /// <summary>
        /// 验证授权码
        /// 用掉一次后,一定要消费掉,确保只能用一次。
        /// </summary>
        /// <param name="userInfo">当前用户信息</param>
        /// <param name="code">授权码</param>
        /// <param name="openId">用户唯一识别码</param>
        /// <returns>验证成功</returns>
        public static bool VerifyAuthorizationCode(BaseUserInfo userInfo, string code, out string openId)
        {
            bool result = false;
            openId = string.Empty;

            if (userInfo != null && !ServiceUtil.VerifySignature(userInfo))
            {
                return result;
            }

            using (var redisClient = PooledRedisHelper.GetTokenClient())
            {
                // 2016-03-03 吉日嘎拉 让缓存早点儿失效
                string key = "code:" + code;
                openId = redisClient.Get<string>(key);
                if (!string.IsNullOrEmpty(openId))
                {
                    result = true;
                    if (userInfo != null && !string.IsNullOrEmpty(userInfo.OpenId))
                    {
                        result = userInfo.OpenId.Equals(openId);
                    }
                }
                redisClient.Remove(key);
            }

            return result;
        }
复制代码
复制代码

返回数据结果类

复制代码
复制代码
    /// <summary>
    /// BaseResult  JsonResult<T>
    /// 
    /// 修改记录
    /// 
    ///        2016.05.12 版本:2.1 JiRiGaLa 增加 Signature 数字签名。
    ///        2016.01.07 版本:2.0 JiRiGaLa 增加 RecordCount。
    ///        2015.11.16 版本:1.1 SongBiao 增加JsonResult<T> 泛型 可以带数据返回。
    ///        2015.09.16 版本:1.1 JiRiGaLa Result 修改为 Status。
    ///        2015.09.15 版本:1.0 JiRiGaLa 添加返回标准定义。
    ///        
    /// <author>
    ///        <name>JiRiGaLa</name>
    ///        <date>2016.05.12</date>
    /// </author> 
    /// </summary>
    [Serializable]
    public class BaseResult
    {
        /// <summary>
        /// 操作是否成功
        /// 2015-09-16 吉日嘎拉 按宋彪建议进行修正
        /// </summary>
        public bool Status = false;

        /// <summary>
        /// 返回值
        /// </summary>
        public string ResultValue = "";

        /// <summary>
        /// 返回状态代码
        /// </summary>
        public string StatusCode = "UnknownError";

        /// <summary>
        /// 返回消息内容
        /// </summary>
        public string StatusMessage = "未知错误";

        /// <summary>
        /// 查询分页数据时返回记录条数用
        /// </summary>
        public int RecordCount = 0;

        /// <summary>
        /// 数字签名(防止篡改)
        /// </summary>
        public string Signature = string.Empty;

        /// <summary>
        /// 对登录的用户进行数字签名
        /// </summary>
        /// <param name="userInfo">登录信息</param>
        /// <returns>进行过数字签名的用户登录信息</returns>
        public string CreateSignature(BaseUserInfo userInfo)
        {
            if (userInfo != null)
            {
                if (!string.IsNullOrEmpty(userInfo.Signature))
                {
                    // 需要签名的内容部分
                    string dataToSign = userInfo.Signature + "_"
                        + ResultValue + "_"
                        + Status.ToString() + "_"
                        + StatusCode.ToString() + "_"
                        + BaseSystemInfo.SecurityKey + "_";
                    // 进行签名
                    Signature = DotNet.Utilities.SecretUtil.md5(dataToSign);
                }
            }

            return Signature;
        }

        /// <summary>
        /// 对登录的用户进行数字签名
        /// </summary>
        /// <param name="userInfo">登录信息</param>
        /// <returns>进行过数字签名的用户登录信息</returns>
        public bool VerifySignature(BaseUserInfo userInfo)
        {
            bool result = false;

            if (userInfo != null)
            {
                if (!string.IsNullOrEmpty(userInfo.Signature))
                {
                    // 需要签名的内容部分
                    string dataToSign = userInfo.Signature + "_"
                        + ResultValue + "_"
                        + Status.ToString() + "_"
                        + StatusCode.ToString() + "_"
                        + BaseSystemInfo.SecurityKey + "_";
                    // 进行签名
                    result = Signature == DotNet.Utilities.SecretUtil.md5(dataToSign);
                }
            }

            return result;
        }
    }

    /// <summary>
    /// Json格式带返回数据
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    public class JsonResult<T> : BaseResult
    {
        public T Data { get; set; }
    }
分享到:
评论

相关推荐

    API接签名验证

    API接口签名验证是一种重要的安全措施,它主要用于保护HTTP RESTful API接口不被未经授权的第三方滥用。在公网环境中,API接口如果不进行适当的保护,可能会遭受各种恶意攻击,如数据篡改、中间人攻击等。签名验证...

    开放API接口签名验证,让你的接口从此不再裸奔.docx

    开放 API 接口签名验证,让你的接口从此不再裸奔 开放 API 接口签名验证是指在开放平台上,对 API 接口进行身份验证和参数签名验证,以确保接口安全和防止篡改参数。该方法使用 AccessKey 和 SecretKey 两者结合,...

    springboot实现接口签名

    在IT行业中,接口签名是一种确保数据安全传输的重要机制,特别是在微服务架构中,如Spring Boot应用与其他系统进行数据交互时。接口签名的主要目的是验证请求的来源合法性,防止数据被篡改,以及确保通信双方的数据...

    API接口对接生成签名与验证签名

    接收端收到数据后,也需要按照相同的步骤进行签名验证,以确保数据的完整性和安全性。 1. **解密参数**: - 对接收到的数据进行解密。 - 示例代码如下: ```php $decrypted_data = json_decode(openssl_decrypt...

    基于Java语言的API接口签名验证与数据安全设计源码

    该项目是一款基于Java语言的API接口签名验证与数据安全设计方案源码,包含114个文件,涵盖97个Java源文件、10个XML配置文件、以及其他多种类型文件,旨在有效解决API接口的数据安全问题。

    Spring Boot实现接口签名验证

    包括配置签名密钥、定义签名算法、拦截器或过滤器实现、测试接口、模拟第三方调用 ...签名校验通常涉及对请求参数的签名计算和验证,以确保请求是由可信的发送方发送,并且在传输过程中没有被篡改。

    Python-Api签名验证样例

    在API(应用程序编程接口)开发中,为了确保数据的安全传输,通常会采用签名验证机制。本文将详细探讨Python中API签名验证的相关知识点,以"Python-Api签名验证样例"为例,结合`flask-apiSign-demo-master`这个...

    拒绝接口裸奔!开放 HTTP API 接口签名验证!.zip

    计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料 计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料 计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料 计算机技术、IT咨询、人工智能AI理论介绍,...

    WEB API 接口签名验证入门与实战课程

    通过对API接口编写与请求过程的分析认识到接口请求中的安全问题,并利用签名验证解决这些问题 这些 API数据传输各种安全问题包括: 1、如何接口请求这的合法性 2、如何保证登陆的安全性 3、如何保证请求的参数不被...

    基于Laravel 8.x的API接口签名认证系统.7z

    在本套课程里,我将带领大家基于laravel 8.x来开发用户认证系统与接口签名验证系统;带领同学们认识Laravel用户认证的两大核心要素,守卫者与数据提供者,并从源码层面分析用户认证中涉及到的核心概念,通过基于接口...

    百度小程序支付签名与验签代码(C#)

    如果两者相等,那么签名验证通过,可以信任此次通知。 在Baiduapplets压缩包文件中,可能包含了具体的C#代码实现,包括订单创建请求、支付回调处理以及签名和验签的相关逻辑。开发者可以参考这些代码来快速集成百度...

    PHP开发API接口签名生成及验证操作示例

    本文实例讲述了PHP开发API接口签名生成及验证操作。分享给大家供大家参考,具体如下: 开发过程中,我们经常会与接口打交道,有的时候是调取别人网站的接口,有的时候是为他人提供自己网站的接口,但是在这调取的...

    WebApi 使用TOKEN+签名验证

    "WebApi使用TOKEN+签名验证"是一种常见的安全策略,它结合了令牌(Token)验证和签名机制,以确保只有授权的客户端能够访问服务。下面将详细介绍这两种方法及其在实际应用中的实现。 1. **令牌(Token)验证**: ...

    接口签名算法设计、MD5签名算法

    在IT行业中,接口签名是一种确保数据完整性和身份验证的关键机制,尤其在分布式系统和API交互中扮演着重要角色。本文将深入探讨接口签名的设计、MD5签名算法以及它们在实际应用中的实现。 接口签名的主要目的是防止...

    Android so文件签名验证源代码

    总结起来,Android SO文件签名验证是保护应用安全的重要手段,它涉及到NDK开发、JNI接口和应用签名机制。通过理解和实践签名验证源代码,开发者可以更好地保障应用的完整性和安全性,防止恶意代码注入。"SignTest...

    PHP 文章集锦,浮点数搞定度运算、签名验证、单点登录、安全防御、缓存技术、RPC、Composer

    │ ├─ 02-PHP 接口签名验证 │ ├─ 03-SSO 单点登录 │ ├─ 04-PHP WEB 安全防御 │ ├─ 05-PHP 缓存技术 │ ├─ 06-三个水桶等分8升水的问题 -《算法的乐趣》 │ ├─ 07-使用过Redis,我竟然还不知道Rdb │ ...

    微信小程序-微信小程序-RSA-签名-验签-加密-解密

    &gt; 一个适用于微信小程序的RSA签名库。 RSA签名的小程序DEMO:https://github.com/zhangzhaopds/WeixinApp_RSA_Signature.git 使用 1、引入文件 var RSA = require('../../utils/wxapp_rsa.js') 2、调用 var private...

    国密签名和验证签名的例子

    如果签名验证成功,接收者就能确信数据未经篡改,并且来自预期的发送者。 在实际的代码实现中,可能会遇到兼容性问题。由于这个例子是为VS2008设计的,如果尝试在更高版本的Visual Studio中编译,可能会遇到问题,...

Global site tag (gtag.js) - Google Analytics