顾陌 发布时间:2017-11-28 分类:记事 阅读:10708次 添加评论
本规范旨在为公司各业务系统之间业务复用及整合的API提供接口调用与交互规范。同时,也作为未来公司业务系统内各应用模块之间以及各业务系统之间,基于面向服务的架构,以服务接口的方式,提供数据和各种功能的一种尝试。
缩写 | 全称 | 说明 |
本规范仅适用于由服务器端发起调用请求、POST提交数据以及GET请求文本数据结果的API,统一采用UTF-8编码规则,采用JSON格式响应。
API 服务接口应提供REST风格的HTTP(HTTPS) 接口:
{protocol}://{domain}:{port}/{type}/{function}/{action}?{query}
变量 | 含义 | 示例 |
protocol | 接口协议 | HTTP、HTTPS |
domain | 网关ip地址或者网关域名 | api.xxx.com |
port | 网关端口号 | 80、8080 |
Type | 功能类别 | SMSManage、UserManage等 |
function | 功能名称 | UserInfo、DepartmentInfo |
action | 操作类别(可空) | CREATE、SELECT、UPDATE、DELETE等 |
query由系统级参数部分和具体API调用参数部分组成,以key1=value1&key2=value2&…表示。
对于采用POST请求的Open API,query部分则是在POST请求体里。
必填 | 描述 | |
Content-Type | 是 | application/x-www-form-urlencoded;charset=utf-8; 或application/json;charset=utf-8; |
名称 | 必填 | 描述 |
appid | 是 | 请求账号 |
timestamp | 是 | Unix时间戳,即从1970年1月1日0时0分0秒开始所经过的秒数(安全验证有效期60秒,防replay攻击) |
sign_type | 否 | 安全验证方式,默认为MD5。 |
sign | 是 | 加密字符。 |
名称 | 类型 | 描述 |
pageindex | Int | 分页索引,默认为1 |
pagesize | Int | 分页量,表示每一页返回多少条数据,默认10,上限50 |
系统提供MD5加密方式调用,默认为加密方式sign_type为MD5。MD5的加密步骤为:
步骤一:构造加密前的字符串
调用方将除“sign”以外的GET请求参数按照参数名称(Key),进行字典升序排序,并将Api接口地址(apiname)和排序后的参数用&拼接起来,参数的值需使用URL编码。
例如:/user/info/select?appid=123456×tamp=1361461671。
步骤二:生成加密字符
使用MD5将步骤一生成的字符串+“&secret=秘钥”加密,并将加密后的字符串转换为小写。
执行完上述步骤,即可获取加密字符串,作为sign的值。
static void Main(string[] args)
{
string domain = "http://api.test.com";
string appid = "123456";
string secret = "XXXXXXXXXXXXX";
int timestamp = GetNowTimeStamp();
//准备参数
SortedDictionary<string, string> paramlist = new SortedDictionary<string, string>();
paramlist.Add("appid", appid);
paramlist.Add("username", "测试字段");
paramlist.Add("timestamp", timestamp.ToString());
string URL = domain + GetSignURL(secret, "/user/info/select", paramlist);
//string result = HttpPost(URL, "");
System.Console.WriteLine(URL);
}
/// <summary>
/// 获取加密后的请求地址
/// </summary>
/// <param name="secret"></param>
/// <param name="apiname"></param>
/// <param name="paramlist"></param>
/// <returns></returns>
public static string GetSignURL(string secret, string apiname, SortedDictionary<string, string> paramlist)
{
string queryString = "";
//拼接字符串
foreach (KeyValuePair<string, string> d in paramlist)
{
queryString += d.Key + "=" + HttpUtility.UrlEncode(d.Value) + "&";
}
string stringSignTemp = apiname + "?" + queryString.TrimEnd('&');
string sign = GetMd5(stringSignTemp + "&secret=" + secret) .ToLower();
return stringSignTemp + "&sign=" + sign;
}
/// <summary>
/// 获取时间戳
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
public static int GetTimeStamp(System.DateTime time)
{
System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
return (int)(time - startTime).TotalSeconds;
}
public static int GetNowTimeStamp()
{
return GetTimeStamp(DateTime.Now);
}
/// <summary>
/// 获取字符串的MD5哈希值,默认编码为
/// </summary>
public static string GetMd5(string value, Encoding encoding = null)
{
if (encoding == null)
{
encoding = Encoding.UTF8;
}
byte[] bytes = encoding.GetBytes(value);
return GetMd5(bytes);
}
/// <summary>
/// 获取字节数组的MD5哈希值
/// </summary>
public static string GetMd5(byte[] bytes)
{
StringBuilder sb = new StringBuilder();
System.Security.Cryptography.MD5 hash = new System.Security.Cryptography.MD5CryptoServiceProvider();
bytes = hash.ComputeHash(bytes);
foreach (byte b in bytes)
{
sb.AppendFormat("{0:x2}", b);
}
return sb.ToString();
}
/// <summary>
/// HTTP请求
/// </summary>
/// <param name="url"></param>
/// <param name="param"></param>
/// <param name="ContentType"></param>
/// <returns></returns>
public static string HttpPost(string url, string param, string ContentType = "application/x-www-form-urlencoded")
{
var result = string.Empty;
//注意提交的编码 这边是需要改变的 这边默认的是Default:系统当前编码
byte[] postData = Encoding.UTF8.GetBytes(param);
// 设置提交的相关参数
System.Net.HttpWebRequest request = System.Net.WebRequest.Create(url) as System.Net.HttpWebRequest;
request.Method = "POST";
request.KeepAlive = false;
request.AllowAutoRedirect = true;
request.ContentType = ContentType;
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)";
request.ContentLength = postData.Length;
// 提交请求数据
System.IO.Stream outputStream = request.GetRequestStream();
outputStream.Write(postData, 0, postData.Length);
outputStream.Close();
System.Net.HttpWebResponse response;
System.IO.Stream responseStream;
System.IO.StreamReader reader;
string srcString;
response = request.GetResponse() as System.Net.HttpWebResponse;
responseStream = response.GetResponseStream();
reader = new System.IO.StreamReader(responseStream, Encoding.GetEncoding("UTF-8"));
srcString = reader.ReadToEnd();
result = srcString; //返回值赋值
reader.Close();
return result;
}
通过请求的参数,多步验证。
◆ 验证请求参数是否完整。
◆ 验证时间戳是否有效(安全验证有效期60秒)。
◆ 根据业务需求添加是否验证请求频次。
◆ 根据业务需求验证时间戳是否已经请求过。
◆ 根据业务需求验证IP限制、时间限制等。
◆ 验证加密字符串。
建议将安全验证应用在MVC的过滤器里。
/// <summary>
/// API请求 安全验证过滤器
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public class SafeTokenAuthAttribute : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
string appid = request.QueryString["appid"];
if (string.IsNullOrWhiteSpace(appid))
{
filterContext.Result = new JsonResult() { Data = new ApiResult() { code = 401, message = "请求账号为空" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
base.OnActionExecuting(filterContext);
return;
}
string sign = request.QueryString["sign"];
if (string.IsNullOrWhiteSpace(sign))
{
filterContext.Result = new JsonResult() { Data = new ApiResult() { code = 402, message = "加密字符不能为空" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
base.OnActionExecuting(filterContext);
return;
}
string timestampStr = request.QueryString["timestamp"] ?? "0";
int timestamp = 0;
int.TryParse(timestampStr, out timestamp);
//当前时间戳
int nowtimestamp = CommonHelper.GetNowTimeStamp();
//调试开发期间暂时禁用
if ((nowtimestamp - timestamp) > 120 || (nowtimestamp - timestamp) < -60)//前后1分钟的有效期
{
filterContext.Result = new JsonResult() { Data = new ApiResult() { code = 403, message = "请求已过期" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
base.OnActionExecuting(filterContext);
return;
}
var filterResult = new JsonResult() { Data = new ApiResult() { code = 400, message = "验证未通过" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
var apiname = request.Url.AbsolutePath;
string queryString = "";
System.Collections.Specialized.NameValueCollection coll = request.QueryString;
String[] requestItem = coll.AllKeys;
for (var i = 0; i < requestItem.Length; i++)
{
if (requestItem[i] != "sign")
{
queryString += requestItem[i] + "=" + HttpUtility.UrlEncode(coll[requestItem[i]]) + "&";
}
}
string sign_type = request.QueryString["sign_type"] ?? "MD5";
//通过appid从数据库或缓存获取对应的secret todo
string secret = "XXXXXXXXXXXXX";
bool isValidate = false;
if (sign_type == "MD5")
{
string stringSignTemp = apiname + "?" + queryString.TrimEnd('&');
string encrypt =GetMd5(stringSignTemp + "&secret=" + secret).ToLower();
if (encrypt == sign)
{
//MD5验证通过
isValidate = true;
}
else
{
filterContext.Result = filterResult;
base.OnActionExecuting(filterContext);
return;
}
}
//未通过验证
if (!isValidate)
{
filterContext.Result = filterResult;
base.OnActionExecuting(filterContext);
return;
}
}
}
MVC的过滤器的使用方法:
[SafeTokenAuth]
public ActionResult Index()
{
return Content("验证通过才能访问");
}
服务端正常输入应该符合如下的JSON格式:
◆ http响应头中的Content-Type指定为application/json, charset=utf-8
◆ 字符串编码格式是UTF-8。
◆ 输出格式为:
{
"code": "响应码(数值型)":,
"message": "响应描述(字符串)",
"data":"[response body](JSON字符串)"
}
[response body]可为空。
API接口请求成功时候响应示例:
{ "code": 200, "message": "请求成功", "data": null } |
API接口请求失败时候响应示例:
{ "code": 402, "message": "请求已过期", "data": null } |
名称 | 类型 | 描述 |
200 | Int | 请求成功 |
100 | Int | 1** 表示请求的参数错误等问题 |
300 | Int | 3** 表示业务逻辑等问题 |
400 | Int | 4** 表示安全验证等问题 |
500 | Int | 5** 表示内部错误等问题 |
名称 | 描述 |
200 | 请求成功 |
400 | 安全验证未通过 |
401 | 请求账号为空 |
402 | 加密字符为空 |
403 | 请求已过期 |
发表评论:
◎欢迎您的参与讨论。