HTTP使用BASIC认证,WebAPI使用[HTTPBasicAuthorize]标记控制器就是使用了BASIC认证。 BASIC认证的缺点HTTP基本认证的目标是提供简单的用户验证功能,其认证过程简单明了,适合于对安全性要求不高的 系统或设备中,如大家所用路由器的配置页面的认证,几乎 都采取了这种方式。其缺点是没有灵活可靠的认证策略,如 无法提供域(domain或realm)认证功能,另外,BASE64的加密强度非常低,可以说仅 能防止sohu的搜索把它搜到了。 当然,HTTP基本认证系统也可以与SSL或者Kerberos结合,实现安全性能较高(相对)的认证系统

难得的吐槽

逃回二线成都呆了两年一直在做休闲娱乐行业的传统管理软件,由于该公司老板太过于独裁,反正股份分红无望,干得不爽。于是乎彻底逃出老家的县城了。 半年来一直做了份远程的工作,非常感恩现在的BOSS,源了我在家办公的心愿,虽然收入下降。 但是好在生活成本降低了许多,就是还个房贷和基本生活开销、两个宝贝上学的开销。 还有就是接了一些私活,最近终于有空打算实现自己的产品梦了。我要做一套通用销售计费软件,原型已经做得七七八八了,就是架构上是单商户的,不是多租户的。 现在计划是分布式应用架构,分桌面程序端,安卓端,商户平台端。由于前段时间客户的用webapi和socket服务端做的中间件在ECS云主机上不断被攻击,有一次居然把中间件程序搞死了。 所以这次使用WebAPI需要考虑使用安全防护机制了。 由于我这是个人的项目,太高深的安全防护肯定也是有门槛的。借鉴了银联POS协议,于是开始了这次的实践。

主要验证流程设计

1.客户端 AuthenticationHeaderValue 请求的头部

客户端请求,规划为 app_id:token,如下面例子就是在服务器端使用"Request.Headers.Authorization.Parameter" 来获取这个值,当然不能是明文吧,就是简单的用Base64处理了下。

  1. using (var httpClient = new HttpClient())
  2. {
  3. var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", "datacool_winform", "27C68F9A899842A598DDBACD2806FDD7")));
  4. httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
  5. string url = "http://" + MiddlewareIP + ":5990" + "/api/CloudPOS/GetVersion?k=" + Guid.NewGuid().ToString();
  6. try
  7. {
  8. string requestResult = httpClient.GetStringAsync(url).Result;
  9. return requestResult;
  10. }
  11. catch (Exception ex)
  12. {
  13. Com.DataCool.DotNetExpand.LogHelper.Error(ex);
  14. return string.Empty;
  15. }
  16. }

2.后台分需要授权验证和不需要授权验证的2个控制器

比如申请软件试用,提交商户门店信息等就是不需要认证就可以发起请求,所以需要2个控制器

3.后台在数据库里控制和校验请求头部的app_id,token

后台就是写一个类实现AuthorizeAttribute,即【HTTPBasicAuthorize】标示的拦截,代码如下:

  1. public class HTTPBasicAuthorizeAttribute : AuthorizeAttribute
  2. {
  3. /// <summary>
  4. /// 校验Authorization
  5. /// </summary>
  6. /// <param name="actionContext"></param>
  7. public override void OnAuthorization(HttpActionContext actionContext)
  8. {
  9. if (actionContext.Request.Headers.Authorization != null)
  10. {
  11. string[] agent_info = Encoding.Default.GetString(Convert.FromBase64String(actionContext.Request.Headers.Authorization.Parameter)).Split(":".ToArray());
  12. //没有按照预设的规则也是视为无权
  13. if (agent_info.Length != 2)
  14. {
  15. HandleUnauthorizedRequest(actionContext);
  16. return;
  17. }
  18. string request_agent = agent_info.FirstOrDefault();
  19. string token = agent_info.LastOrDefault();
  20. #region 数据库校验app_id和token
  21. using (var db = new POS_DB())
  22. {
  23. try
  24. {
  25. db.Database.CreateIfNotExists();
  26. }
  27. catch { }
  28. #region 默认授权
  29. if (!db.sys_api_authorize.Any())
  30. {
  31. var dt = DateTime.Now;
  32. var sys_scheme = new sys_api_authorize
  33. {
  34. merchant_name = "DataCool",
  35. request_scheme = "afeng124",
  36. request_token = "15730052377",
  37. master_key = Guid.NewGuid().ToString().Replace("-", ""),
  38. create_dt = dt,
  39. last_request_dt = dt,
  40. status = 1
  41. };
  42. db.sys_api_authorize.Add(sys_scheme);
  43. db.Entry<sys_api_authorize>(sys_scheme).State = System.Data.Entity.EntityState.Added;
  44. db.SaveChanges();
  45. }
  46. #endregion
  47. var scheme_entity = db.sys_api_authorize
  48. .Where(s => s.request_scheme == request_agent && s.request_token == token && s.status == 1)
  49. .FirstOrDefault();
  50. if (scheme_entity != null)
  51. {
  52. scheme_entity.last_request_dt = DateTime.Now;
  53. db.SaveChanges();
  54. IsAuthorized(actionContext);
  55. }
  56. else
  57. {
  58. HandleUnauthorizedRequest(actionContext);
  59. }
  60. }
  61. #endregion
  62. }
  63. else
  64. {
  65. HandleUnauthorizedRequest(actionContext);
  66. }
  67. }
  68. /// <summary>
  69. /// 未通过认证,日志进行记录(发起请求的IP,请求的方法)
  70. /// </summary>
  71. /// <param name="actionContext"></param>
  72. protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
  73. {
  74. var challengeMessage = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
  75. challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
  76. if (actionContext.Request.Headers.Authorization == null)
  77. {
  78. string ip = actionContext.Request.GetClientIpAddress();
  79. var request_url = actionContext.Request.RequestUri.AbsoluteUri.ToString();
  80. var request_obj = new
  81. {
  82. RequestIP = ip,
  83. Request_Action = request_url,
  84. ErrorDesc = challengeMessage.StatusCode.ToString(),
  85. RequestMethod = actionContext.Request.Method.ToString(),
  86. Controller = actionContext.ControllerContext.ControllerDescriptor.ControllerName,
  87. RequestUrl = actionContext.Request.RequestUri.AbsoluteUri.ToString()
  88. };
  89. Com.DataCool.DotNetExpand.LogHelper.Error(request_obj);
  90. }
  91. base.HandleUnauthorizedRequest(actionContext);
  92. //throw new HttpResponseException(challengeMessage);
  93. }
  94. }

上面如果直接throw会导致宿主服务程序异常,没想到的是如果直接交给父类处理就行了。

没通过认证那么客户端调用会出异常:

  1. 2016-10-09 17:02:39,916 级别:ERROR 日志描述:System.AggregateException: 发生一个或多个错误。 ---> System.Net.Http.HttpRequestException: 响应状态代码不指示成功: 401 (Unauthorized)。
  2. --- 内部异常堆栈跟踪的结尾 ---
  3. System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
  4. System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
  5. 在 System.Threading.Tasks.Task`1.get_Result()
  6. MiddlewareService.MiddlewareServiceSvr.HttpAPIRequest() 位置 D:\cloudservice\WebAPIService\MiddlewareServiceSvr.cs:行号 97
  7. ---> (内部异常 #0) System.Net.Http.HttpRequestException: 响应状态代码不指示成功: 401 (Unauthorized)。

浏览器模拟get的样子是这样:

    

4.改造软件的推广方式和BASIC认证结合起来

这个思路主要是这样的:

1.商户在官网上下载客户端软件前先申请试用,提交商户的基本资料,这是营销和售前服务的基础。

2.商户提交试用申请后下载桌面程序,桌面程序激活一下调用api获取主密钥和app_id和token。

3.登录商户后台设置门店基础参数。

4.服务器可以控制使用期限和功能

活学活用,webapi HTTPBasicAuthorize搭建小型云应用的实践的更多相关文章

  1. JVM活学活用——调优工具

    概述 工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗费性能的分析(dump文件分析)一般也不会在生产直接分析,往往dump下来的文件达1G左右,人工分析效率较低,因此利用工具来分析jvm相 ...

  2. pandas pivot_table 活学活用实例教程

    pandas pivot_table 活学活用实例教程 导入相关数据分析的库 首先进行commentTime时间进行数据预处理 查看数据类型信息 最简单的透视表 直接敲击该函数,在notebook中可 ...

  3. HTML5--details活学活用

    这是补充HTML5基础知识的系列内容,其他为: 一.HTML5-- 新的结构元素 二.HTML5-- figure.time.details.mark 三.HTML5-- details活学活用 四. ...

  4. JVM活学活用——优化springboot

    介绍 在SpringBoot的Web项目中,默认采用的是内置Tomcat,当然也可以配置支持内置的jetty,内置有什么好处呢? 1. 方便微服务部署. 2. 方便项目启动,不需要下载Tomcat或者 ...

  5. 活学活用wxPython基础框架

    看活活用wxpython这本书,基本框架是这样子的,这里有定义输出,然后打印出整个流程,可以看到是怎样执行的,明天请假了,五一回去玩几天,哈哈,估计假期过来都忘了 import wx import s ...

  6. JVM活学活用——GC算法 垃圾收集器

    概述 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本地方 ...

  7. JVM活学活用——类加载机制

    类的实例化过程 有父类的情况 1. 加载父类静态    1.1 为静态属性分配存储空间并赋初始值     1.2 执行静态初始化块和静态初始化语句(从上至下) 2. 加载子类静态    2.1 为静态 ...

  8. JVM活学活用——Jvm内存结构

    Java内存结构: JVM内存结构主要是有三大块:堆内存.方法区和栈.堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分为三部分,Eden空间.From Survivor空间.To S ...

  9. 活学活用,CSS清除浮动的4种方法

    清除浮动这个问题,做前端的应该再熟悉不过了,咱是个新人,所以还是记个笔记,做个积累,努力学习向大神靠近. CSS清除浮动的方法网上一搜,大概有N多种,用过几种,说下个人感受. 1.结尾处加空div标签 ...

随机推荐

  1. [51单片机] EEPROM AT24c02 [存储\读取一个字节]

    /*----------------------------------------------- 名称:IIC协议 EEPROM24c02 存数读取数据 内容:此程序用于检测EEPROM性能,测试方 ...

  2. [BTS] WCF-OracleDB

    When I insert some data to Oracle, BizTalk WCF-OracleDB throw this error. A message sent to adapter ...

  3. 构建单页Web应用

    摘自前端农民工的博客 让我们先来看几个网站: coding teambition cloud9 注意这几个网站的相同点,那就是在浏览器中,做了原先“应当”在客户端做的事情.它们的界面切换非常流畅,响应 ...

  4. clientHeight,offsetHeight与scrollHeight的相关知识

    在html里,width与height是最常用也是最基础的两个属性,因此,在js里,我们也经常需要操作这两个属性.js关于这两个属性提供了client*,offset*与scroll*,很多同学搞不清 ...

  5. Gershgorin圆盘定理

    众所周知,对一个$n$阶方阵求取特征值需要解一个一元$n$次方程,当$n$很大时,这是很难实现的.但是,在有些涉及矩阵的实际问题中,我们并不需要知道矩阵特征值的准确值,而只需要知道其大概范围就行了,例 ...

  6. phpcms v9二次开发笔记

    phpcms是基于MVC结构的. 安装: 下载phpcms_v9.5.9_UTF8.zip:新建目录phpcms,将压缩包里install_package目录下所有文件复制到phpcms目录.浏览器输 ...

  7. Java程序员的日常 —— 工作一天的收获

    看题目可能是扯皮,其实还是有很多专业知识的.从最开始没有注意到设计原则,到后面的jquery实战技巧,都是今天一天碰到的问题. 每天整理一点点,每天收获一点点. 关于软件设计 在设计系统结构的时候,一 ...

  8. Atitit.现实生活中最好使用的排序方法-----ati排序法总结

    Atitit.现实生活中最好使用的排序方法-----ati排序法总结 1. 现在的问题 1 2. 排序的类别::插入排序//交换排序//选择排序(每次最小/大排在相应的位置  )//归并排序//基数排 ...

  9. Java集合——题目

    第一题 (Map)利用Map,完成下面的功能: 从命令行读入一个字符串,表示一个年份,输出该年的世界杯冠军是哪支球队.如果该 年没有举办世界杯,则输出:没有举办世界杯. 附:世界杯冠军以及对应的夺冠年 ...

  10. jQuery实现左移右移

    <html> <head> <meta charset="utf-8"> <title>完成左移右移</title> & ...