http://blog.csdn.net/qq289523052/article/details/47750021

秘钥是双方约定的,并不在网络连接上传输

2015-08-18 12:03 15781人阅读 评论(1) 收藏 举报
分类:
Web API

目录(?)[+]

1、Web API的接口访问分类

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

1)一个是使用用户令牌,通过Web API接口进行数据访问。这种方式,可以有效识别用户的身份,为用户接口返回用户相关的数据,如包括用户信息维护、密码修改、或者用户联系人等与用户身份相关的数据。

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

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

下面图示就是这几种接入方式的说明和大概应用场景。

2、Web API使用安全签名的实现

首先我们为用户注册的时候,需要由我们认可的终端发起,也就是它们需要进行安全签名,后台确认签名有效性,才能正常实现用户注册,否则遭到伪造数据,系统就失去原有的意义了。

  1. /// <summary>
  2. /// 注册用户信息接口
  3. /// </summary>
  4. public interface IUserApi
  5. {
  6. /// <summary>
  7. /// 注册用户处理,包括用户名,密码,身份证号,手机等信息
  8. /// </summary>
  9. /// <param name="json">注册用户信息</param>
  10. /// <param name="signature">加密签名字符串</param>
  11. /// <param name="timestamp">时间戳</param>
  12. /// <param name="nonce">随机数</param>
  13. /// <param name="appid">应用接入ID</param>
  14. /// <returns></returns>
  15. ResultData Add(UserJson json,
  16. string signature, string timestamp, string nonce, string appid);
  17. }

其实我们获得用户的令牌,也是需要进行用户安全签名认证的,这样我们才有效保证用户身份令牌获取的合法性。

  1. /// <summary>
  2. /// 系统认证等基础接口
  3. /// </summary>
  4. public interface IAuthApi
  5. {
  6. /// <summary>
  7. /// 注册用户获取访问令牌接口
  8. /// </summary>
  9. /// <param name="username">用户登录名称</param>
  10. /// <param name="password">用户密码</param>
  11. /// <param name="signature">加密签名字符串</param>
  12. /// <param name="timestamp">时间戳</param>
  13. /// <param name="nonce">随机数</param>
  14. /// <param name="appid">应用接入ID</param>
  15. /// <returns></returns>
  16. TokenResult GetAccessToken(string username, string password,
  17. string signature, string timestamp, string nonce, string appid);
  18. }

上面介绍到的参数,我们提及了几个参数,一个是加密签名字符串,一个是时间戳,一个是随机数,一个是应用接入ID,我们一般的处理规则如下所示。

1)Web API 为各种应用接入,如APP、Web、Winform等接入端分配应用AppID以及通信密钥AppSecret,双方各自存储。
2)接入端在请求Web API接口时需携带以下参数:signature、 timestamp、nonce、appid,签名是根据几个参数和加密秘钥生成。
3) Web API 收到接口调用请求时需先检查传递的签名是否合法,验证后才调用相关接口。

加密签名在服务端(Web API端)的验证流程参考微信的接口的处理方式,处理逻辑如下所示。

1)检查timestamp 与系统时间是否相差在合理时间内,如10分钟。
2)将appSecret、timestamp、nonce三个参数进行字典序排序
3)将三个参数字符串拼接成一个字符串进行SHA1加密
4)加密后的字符串可与signature对比,若匹配则标识该次请求来源于某应用端,请求是合法的。

C#端代码校验如下所示。

  1. /// <summary>
  2. /// 检查应用接入的数据完整性
  3. /// </summary>
  4. /// <param name="signature">加密签名内容</param>
  5. /// <param name="timestamp">时间戳</param>
  6. /// <param name="nonce">随机字符串</param>
  7. /// <param name="appid">应用接入Id</param>
  8. /// <returns></returns>
  9. public CheckResult ValidateSignature(string signature, string timestamp, string nonce, string appid)
  10. {
  11. CheckResult result = new CheckResult();
  12. result.errmsg = "数据完整性检查不通过";
  13.  
  14. //根据Appid获取接入渠道的详细信息
  15. AppInfo channelInfo = BLLFactory<App>.Instance.FindByAppId(appid);
  16. if (channelInfo != null)
  17. {
  18. #region 校验签名参数的来源是否正确
  19. string[] ArrTmp = { channelInfo.AppSecret, timestamp, nonce };
  20.  
  21. Array.Sort(ArrTmp);
  22. string tmpStr = string.Join("", ArrTmp);
  23.  
  24. tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
  25. tmpStr = tmpStr.ToLower();
  26.  
  27. if (tmpStr == signature && ValidateUtil.IsNumber(timestamp))
  28. {
  29. DateTime dtTime = timestamp.ToInt32().IntToDateTime();
  30. double minutes = DateTime.Now.Subtract(dtTime).TotalMinutes;
  31. if (minutes > timspanExpiredMinutes)
  32. {
  33. result.errmsg = "签名时间戳失效";
  34. }
  35. else
  36. {
  37. result.errmsg = "";
  38. result.success = true;
  39. result.channel = channelInfo.Channel;
  40. }
  41. }
  42. #endregion
  43. }
  44. return result;
  45. }

一旦我们完成对安全签名进行成功认证,也就是我们对数据提交的来源和完整性进行了确认,就可以进行更多和安全性相关的操作了,如获取用户的访问令牌信息的操作如下所示。

第一步是验证用户的签名是否符合要求,符合要求后进行用户信息的比对,并生成用户访问令牌数据JSON,返回给调用端即可。

3、Web API使用安全令牌的实现

通过上面的接口,我们获取到的用户访问令牌,以后和用户相关的信息调用,我们就可以通过这个令牌参数进行传递就可以了,这个令牌带有用户的一些基础信息,如用户ID,过期时间等等,这个Token的设计思路来源于JSON Web Token (JWT),具体可以参考http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html,以及GitHub上的项目https://github.com/jwt-dotnet/jwt

由于Web API的调用,都是一种无状态方式的调用方式,我们通过token来传递我们的用户信息,这样我们只需要验证Token就可以了。

JWT的令牌生成逻辑如下所示

令牌生成后,我们需要在Web API调用处理前,对令牌进行校验,确保令牌是正确有效的。

检查的代码,就是把令牌生成的过程逆反过来,获取相应的信息,并且对令牌签发的时间进行有效性判断,一般可以约定一个失效时间,如1天或者7天,也不用设置太短。

  1. /// <summary>
  2. /// 检查用户的Token有效性
  3. /// </summary>
  4. /// <param name="token"></param>
  5. /// <returns></returns>
  6. public CheckResult ValidateToken(string token)
  7. {
  8. //返回的结果对象
  9. CheckResult result = new CheckResult();
  10. result.errmsg = "令牌检查不通过";
  11.  
  12. if (!string.IsNullOrEmpty(token))
  13. {
  14. try
  15. {
  16. string decodedJwt = JsonWebToken.Decode(token, sharedKey);
  17. if (!string.IsNullOrEmpty(decodedJwt))
  18. {
  19. #region 检查令牌对象内容
  20. dynamic root = JObject.Parse(decodedJwt);
  21. string username = root.name;
  22. string userid = root.iss;
  23. int jwtcreated = (int)root.iat;
  24.  
  25. //检查令牌的有效期,7天内有效
  26. TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
  27. int timestamp = (int)t.TotalDays;
  28. if (timestamp - jwtcreated > expiredDays)
  29. {
  30. throw new ArgumentException("用户令牌失效.");
  31. }
  32.  
  33. //成功校验
  34. result.success = true;
  35. result.errmsg = "";
  36. result.userid = userid;
  37. #endregion
  38. }
  39. }
  40. catch (Exception ex)
  41. {
  42. LogTextHelper.Error(ex);
  43. }
  44. }
  45. return result;
  46. }

一般来说,访问令牌不能永久有效,对于访问令牌的重新更新问题,可以设置一个规则,只允许最新的令牌使用,并把它存储在接口缓存里面进行对比,应用系统退出的时候,就把内存里面的Token移除就可以了。

4、ASP.NET Web API的开发

上面我们定义了一般的Web API接口,以及实现相应的业务实现,如果我们需要创建Web API层,还需要构建一个Web API项目的。

创建好相应的项目后,可以为项目添加一个Web API基类,方便控制共同的接口。

然后我们就可以在Controller目录上创建更多的应用API控制器了。

最后我们为了统一所有的API接口都是返回JSON方式,我们需要对WebApiConfig里面的代码进行设置下。

  1. public static class WebApiConfig
  2. {
  3. public static void Register(HttpConfiguration config)
  4. {
  5. // Web API 配置和服务
  6. config.SetCorsPolicyProviderFactory(new CorsPolicyFactory());
  7. config.EnableCors();
  8.  
  9. // Web API 路由
  10. config.MapHttpAttributeRoutes();
  11.  
  12. config.Routes.MapHttpRoute(
  13. name: "DefaultApi",
  14. routeTemplate: "api/{controller}/{action}/{id}",
  15. defaults: new { action = "post", id = RouteParameter.Optional }
  16. );
  17.  
  18. // Remove the JSON formatter
  19. //config.Formatters.Remove(config.Formatters.JsonFormatter);
  20.  
  21. // Remove the XML formatter
  22. config.Formatters.Remove(config.Formatters.XmlFormatter);
  23. }
  24. }

5、Web API 接口的测试

接下来我们要做的就是需要增加业务接口,以便进行具体的测试了,建议使用Winform项目,对每个接口进行一个测试,或者也可以考虑使用单元测试的方式,看个人喜好吧。

例如我们如果要测试用户登陆的接口的话,我们的测试代码如下所示。

  1. /// <summary>
  2. /// 生成签名字符串
  3. /// </summary>
  4. /// <param name="appSecret">接入秘钥</param>
  5. /// <param name="timestamp">时间戳</param>
  6. /// <param name="nonce">随机数</param>
  7. private string SignatureString(string appSecret, string timestamp, string nonce)
  8. {
  9. string[] ArrTmp = { appSecret, timestamp, nonce };
  10.  
  11. Array.Sort(ArrTmp);
  12. string tmpStr = string.Join("", ArrTmp);
  13.  
  14. tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
  15. return tmpStr.ToLower();
  16. }
  17.  
  18. private TokenResult GetTokenResult()
  19. {
  20. string timestamp = DateTime.Now.DateTimeToInt().ToString();
  21. string nonce = new Random().NextDouble().ToString();
  22. string signature = SignatureString(appSecret, timestamp, nonce);
  23.  
  24. string appended = string.Format("&signature={0}&timestamp={1}&nonce={2}&appid={3}", signature, timestamp, nonce, appId);
  25. string queryUrl = url + "Auth/GetAccessToken?username=test&password=123456" + appended;
  26.  
  27. HttpHelper helper = new HttpHelper();
  28. string token = helper.GetHtml(queryUrl);
  29. Console.WriteLine(token);
  30. TokenResult tokenResult = JsonConvert.DeserializeObject<TokenResult>(token);
  31. return tokenResult;
  32. }

如果我们已经获得了令牌,我们根据令牌传递参数给连接,并获取其他数据的测试处理代码如下所示。

  1. //获取访问令牌
  2. TokenResult tokenResult = GetTokenResult();
  3.  
  4. string queryUrl = url + "/Contact/get?token=" + tokenResult.access_token;
  5. HttpHelper helper = new HttpHelper();
  6. string result = helper.GetHtml(queryUrl);
  7. Console.WriteLine(result);

如果需要POST数据的话,那么调用代码如下所示。

  1. //使用POST方式
  2. var data = new
  3. {
  4. name = "张三",
  5. certno = "123456789",
  6. };
  7. var postData = data.ToJson();
  8.  
  9. queryUrl = url + "/Contact/Add?token=" + tokenResult.access_token;
  10. helper = new HttpHelper();
  11. helper.ContentType = "application/json";
  12. result = helper.GetHtml(queryUrl, postData, true);
  13. Console.WriteLine(result);

Web API后台,会自动把POST的JSON数据转换为对应的对象的。

如果是GET方式,我们可能可以直接通过浏览器进行调试,如果是POST方式,我们需要使用一些协助工具,如Fiddler等处理工具,但是最好的方式是自己根据需要弄一个测试工具,方便测试。

以下就是我为了自己Web API 接口开发的需要,专门弄的一个调试工具,可以自动组装相关的参数,包括使用安全签名的参数,还可以把所有参数数据进行存储。

转载自:点击打开链接

Web API 令牌(秘钥是双方约定的,并不在网络连接上传输)的更多相关文章

  1. WebApi安全性 使用TOKEN+签名验证 (秘钥是GUID的,私有的,不是雙方的,并不在网络连接上传输)

    转http://www.cnblogs.com/MR-YY/archive/2016/10/18/5972380.html WebApi安全性 使用TOKEN+签名验证   首先问大家一个问题,你在写 ...

  2. 【ASP.NET Web API教程】5.3 发送HTML表单数据:文件上传与多部分MIME

    原文:[ASP.NET Web API教程]5.3 发送HTML表单数据:文件上传与多部分MIME 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本系列教程,请先看前面 ...

  3. web api 如何通过接收文件流的方式,接收客户端及前端上传的文件

    服务端接收文件流代码: public async Task<HttpResponseMessage> ReceiveFileByStream() { var stream = HttpCo ...

  4. Web API系列(二)接口安全和参数校验

    以前简单介绍过web api 的设计,但是还是有很多朋友问我,如何合理的设计和实现web api.比如,接口安全,异常处理,统一数据返回等问题.所以有必要系统的总结总结 web api 的设计和实现. ...

  5. Web API数据传输加密

    http://www.cnblogs.com/wuhuacong/p/4620300.html Web API应用架构设计分析(2) 在上篇随笔<Web API应用架构设计分析(1)>, ...

  6. Web API应用架构在Winform混合框架中的应用(1)

    在<Web API应用架构设计分析(1)>和<Web API应用架构设计分析(2)>中对WebAPI的架构进行了一定的剖析,在当今移动优先的口号下,传统平台都纷纷开发了属于自己 ...

  7. Web API应用架构设计分析(2)

    在上篇随笔<Web API应用架构设计分析(1)>,我对Web API的各种应用架构进行了概括性的分析和设计,Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端 ...

  8. Web API接口 安全验证

    在上篇随笔<Web API应用架构设计分析(1)>,我对Web API的各种应用架构进行了概括性的分析和设计,Web API 是一种应用接口框架,它能够构建HTTP服务以支撑更广泛的客户端 ...

  9. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

随机推荐

  1. ES6 class的基本语法-学习笔记

    1.基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰.更像面向对象编程的语法而已. 类的内部所有定义的方法,都是 ...

  2. html播放音乐

    如何在网站网页中添加音乐代码 告诉你多种格式文件的详细使用代码.    width_num——指定一个作为宽度的数字:    height_num——指定一个作为高度的数字:     1.mp3    ...

  3. Unity查找物体的子物体、孙物体

    Unity查找物体下的所有物体 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分 ...

  4. rcnn系列

    提纲挈领 https://blog.csdn.net/linolzhang/article/details/54344350 SPP https://www.cnblogs.com/gongxijun ...

  5. Version Control 版本控制

    一.version control是什么: version control版本控制,是指对软件开发过程中各种程序代码.配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一. 二.versi ...

  6. (0-1)CSS 标签语法的属性

    CSS text-decoration 属性 display display 属性规定元素应该生成的框的类型

  7. canvas 使用 isPointInPath() 判断鼠标位置是否在绘制的元素上

    canvas 里绘制的图形不是一个实体 DOM,所以要给每个绘制的图形添加事件操作比给 DOM 添加事件要复杂很多. 所以,我们需要使用一个 canvas 的 isPointInPath(x, y) ...

  8. MySQL InnoDB Engine--缓冲器数据交换

    通常情况下,缓冲池无法将整个数据库所有数据都进行缓冲,而且不同数据的访问频率不一样,有些数据会被频繁访问,而有些数据可能数月不会被访问一次,因此数据库使用最近最少使用LRU latest Recent ...

  9. vi常用操作

    什么是vi: vi是Linux/Unix底下最常用的文本编辑器,可以理解为和Windows下的txt一样,咱们一般操作linux服务器的时候都是没有图形化界面的, 怎么移动光标,到哪个位置,替换修改什 ...

  10. EMC EMI 自行评估记录

    EMC EMI 自行评估记录 设备 频谱仪 网房 评估 设置频谱频率为 30M 1G. 给频谱仪接一个天线,将被测试的机器上电然后在不同的角度换,看频谱仪上的数值. 可以绕上去,但不要和 PCB接触.