活学活用,webapi HTTPBasicAuthorize搭建小型云应用的实践
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处理了下。
using (var httpClient = new HttpClient()) { var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", "datacool_winform", "27C68F9A899842A598DDBACD2806FDD7"))); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials); string url = "http://" + MiddlewareIP + ":5990" + "/api/CloudPOS/GetVersion?k=" + Guid.NewGuid().ToString(); try { string requestResult = httpClient.GetStringAsync(url).Result; return requestResult; } catch (Exception ex) { Com.DataCool.DotNetExpand.LogHelper.Error(ex); return string.Empty; } }
2.后台分需要授权验证和不需要授权验证的2个控制器
比如申请软件试用,提交商户门店信息等就是不需要认证就可以发起请求,所以需要2个控制器
3.后台在数据库里控制和校验请求头部的app_id,token
后台就是写一个类实现AuthorizeAttribute,即【HTTPBasicAuthorize】标示的拦截,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | public class HTTPBasicAuthorizeAttribute : AuthorizeAttribute { /// <summary> /// 校验Authorization /// </summary> /// <param name="actionContext"></param> public override void OnAuthorization(HttpActionContext actionContext) { if (actionContext.Request.Headers.Authorization != null ) { string [] agent_info = Encoding.Default.GetString(Convert.FromBase64String(actionContext.Request.Headers.Authorization.Parameter)).Split( ":" .ToArray()); //没有按照预设的规则也是视为无权 if (agent_info.Length != 2) { HandleUnauthorizedRequest(actionContext); return ; } string request_agent = agent_info.FirstOrDefault(); string token = agent_info.LastOrDefault(); #region 数据库校验app_id和token using ( var db = new POS_DB()) { try { db.Database.CreateIfNotExists(); } catch { } #region 默认授权 if (!db.sys_api_authorize.Any()) { var dt = DateTime.Now; var sys_scheme = new sys_api_authorize { merchant_name = "DataCool" , request_scheme = "afeng124" , request_token = "15730052377" , master_key = Guid.NewGuid().ToString().Replace( "-" , "" ), create_dt = dt, last_request_dt = dt, status = 1 }; db.sys_api_authorize.Add(sys_scheme); db.Entry<sys_api_authorize>(sys_scheme).State = System.Data.Entity.EntityState.Added; db.SaveChanges(); } #endregion var scheme_entity = db.sys_api_authorize .Where(s => s.request_scheme == request_agent && s.request_token == token && s.status == 1) .FirstOrDefault(); if (scheme_entity != null ) { scheme_entity.last_request_dt = DateTime.Now; db.SaveChanges(); IsAuthorized(actionContext); } else { HandleUnauthorizedRequest(actionContext); } } #endregion } else { HandleUnauthorizedRequest(actionContext); } } /// <summary> /// 未通过认证,日志进行记录(发起请求的IP,请求的方法) /// </summary> /// <param name="actionContext"></param> protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) { var challengeMessage = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); challengeMessage.Headers.Add( "WWW-Authenticate" , "Basic" ); if (actionContext.Request.Headers.Authorization == null ) { string ip = actionContext.Request.GetClientIpAddress(); var request_url = actionContext.Request.RequestUri.AbsoluteUri.ToString(); var request_obj = new { RequestIP = ip, Request_Action = request_url, ErrorDesc = challengeMessage.StatusCode.ToString(), RequestMethod = actionContext.Request.Method.ToString(), Controller = actionContext.ControllerContext.ControllerDescriptor.ControllerName, RequestUrl = actionContext.Request.RequestUri.AbsoluteUri.ToString() }; Com.DataCool.DotNetExpand.LogHelper.Error(request_obj); } base .HandleUnauthorizedRequest(actionContext); //throw new HttpResponseException(challengeMessage); } } |
上面如果直接throw会导致宿主服务程序异常,没想到的是如果直接交给父类处理就行了。
没通过认证那么客户端调用会出异常:
1 2 3 4 5 6 7 | 2016-10-09 17:02:39,916 级别:ERROR 日志描述:System.AggregateException: 发生一个或多个错误。 ---> System.Net.Http.HttpRequestException: 响应状态代码不指示成功: 401 (Unauthorized)。 --- 内部异常堆栈跟踪的结尾 --- 在 System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) 在 System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) 在 System.Threading.Tasks.Task`1.get_Result() 在 MiddlewareService.MiddlewareServiceSvr.HttpAPIRequest() 位置 D:\cloudservice\WebAPIService\MiddlewareServiceSvr.cs:行号 97 ---> (内部异常 #0) System.Net.Http.HttpRequestException: 响应状态代码不指示成功: 401 (Unauthorized)。 |
浏览器模拟get的样子是这样:
4.改造软件的推广方式和BASIC认证结合起来
这个思路主要是这样的:
1.商户在官网上下载客户端软件前先申请试用,提交商户的基本资料,这是营销和售前服务的基础。
2.商户提交试用申请后下载桌面程序,桌面程序激活一下调用api获取主密钥和app_id和token。
3.登录商户后台设置门店基础参数。
4.服务器可以控制使用期限和功能
作者:数据酷软件
出处:https://www.cnblogs.com/datacool/p/datacool_http_basic_auth.html
关于作者:20年编程从业经验,持续关注MES/ERP/POS/WMS/工业自动化
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明。
联系方式: qq:71008973;wx:6857740733
基于人脸识别的考勤系统 地址: https://gitee.com/afeng124/viewface_attendance_ext
自己开发安卓应用框架 地址: https://gitee.com/afeng124/android-app-frame
WPOS(warehouse+pos) 后台演示地址: http://47.239.106.75:8080/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构