项目中常用的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进行对比;

....

// 插叙数据获取列表

如下图

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 = ; i < bytes.Length; i++)
{
string strX2 = bytes[i].ToString("X2");
if (strX2.Substring(, ) == "")
{
strX2 = strX2.Substring(, );
}
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 = ; 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 = ; 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();
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 = ; /// <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接口签名验证参考的更多相关文章

  1. 转载-常用API接口签名验证参考

    原文地址: http://www.cnblogs.com/hnsongbiao/p/5478645.html 写的很好,就做个笔记了.感谢作者! 项目中常用的API接口签名验证方法: 1. 给app分 ...

  2. 常用API接口汇总

    下面列举了100多个国内常用API接口,并按照 笔记.出行.词典.电商.地图.电影.即时通讯.开发者网站.快递查询.旅游.社交.视频.天气.团队协作.图片与图像处理.外卖.消息推送.音乐.云.语义识别 ...

  3. 常用API接口

    引用  常用API接口汇总

  4. 开放api接口签名验证

    不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...

  5. api接口签名验证(MD5)

    不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...

  6. 【转】开放api接口签名验证

    不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...

  7. API接口签名验证2

    http://www.jianshu.com/p/d47da77b6419 系统从外部获取数据时,通常采用API接口调用的方式来实现.请求方和�接口提供方之间的通信过程,有这几个问题需要考虑: 1.请 ...

  8. springcloud提供开放api接口签名验证

    一.MD5参数签名的方式 我们对api查询产品接口进行优化: 1.给app分配对应的key.secret 2.Sign签名,调用API 时需要对请求参数进行签名验证,签名方式如下: a. 按照请求参数 ...

  9. html5视频常用API接口

    一.虽然有的属性是boolean类型,但仍旧建议按照XHTML书写(属性名=”属性值”)格式,避免出现错误 (下面加粗的属性为常用属性) 属性 值 功能描述 controls controls 是否显 ...

随机推荐

  1. Poj1477

    Box of Bricks Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 24101   Accepted: 9378 De ...

  2. 观察者模式与.Net Framework中的委托与事件

    本文文字内容均选自<大话设计模式>一书. 解释:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够 ...

  3. 【修复】当Deepin开机进入BusyBox时修复

    第一次发生这种状况,是因为上一次关机时我直接断电了(并非故意的,我用了deepin一个月出了好几次关机后死机╮(╯▽╰)╭) 参考: 爱之墨色(完美解决)linux 开机进入initramfs无法开机 ...

  4. ASP.NET Core 部署IIS及 OFFSET 附近有语法错误解决

    今天自己开发了一个订机票的微信公众号,功能基本已经完成,然后想部署到服务器实际测试下.结果部署上去出现各种问题.先安装asp.net core模块,然后发现数据库并不像在开发时一样,执行ef的命令行语 ...

  5. 三、Snapman多人协作电子表格之——软件的基本功能

    Snapman多人协作电子表格是一个即时工作系统. 一.SnapmanServer服务端 SnapmanServer服务端在安装Snapman软件一起自带,是一个小巧的控制台程序SnapmanServ ...

  6. axis根据wsdl生成java客户端代码

    根据wsdl生成java客户端代码有多个方法,其中使用axis生成的代码比较友好,也是经常用的一种方法.首先下载axis jar包:axis-bin-1_4.zip 官方地址:http://ws.Ap ...

  7. Linux下LANMP集成环境中编译增加pdo_odbc模块

    linux版本为CentOs6.5,php集成环境为lanmp_v3.1,集成环境中默认的pdo扩展为:mysql, sqlite, sqlite2,现在有需求想链接微软的Access数据库,所以需要 ...

  8. 一分钟了解Allegro导入DXF文件

    Allegro, pads,PCB线路板设计,小北PCB 很高兴与大家分享一分钟了解Allegro导入DXF文件的方法,请问您们,刚学习这个软件时,您是否遇到过同样的问题呢?应该我们每一个刚学习者都会 ...

  9. Scrapy 框架流程详解

    框架流程图 Scrapy 使用了 Twisted 异步非阻塞网络库来处理网络通讯,整体架构大致如下(绿线是数据流向): 简单叙述一下每层图的含义吧: Spiders(爬虫):它负责处理所有Respon ...

  10. DataPipeline创始人&CEO 陈诚:沃森与AI

    引言:本文来自infoQ架构师电子月刊对DataPipeline创始人&CEO陈诚的约稿.陈诚,毕业于上海交大,留学于美国密西根大学,前Yelp大数据研发工程师,曾就职于美国Google.Ye ...