活学活用,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】标示的拦截,代码如下:

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会导致宿主服务程序异常,没想到的是如果直接交给父类处理就行了。

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

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.服务器可以控制使用期限和功能

时间: 2024-11-05 04:52:57

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

pandas pivot_table 活学活用实例教程

pandas pivot_table 活学活用实例教程 导入相关数据分析的库 首先进行commentTime时间进行数据预处理 查看数据类型信息 最简单的透视表 直接敲击该函数,在notebook中可以查看该函数的参数 多个索引列 特定列的统计 规定特定的聚合函数 传入多个聚合函数 传入columns参数 生成的DataFrame可以导出excel或csv文件 修改index中的数据类型,显示完整的索引列 传入fill_value参数,处理缺失值 设添加margins参数,定margin_nam

活学活用wxPython基础框架

看活活用wxpython这本书,基本框架是这样子的,这里有定义输出,然后打印出整个流程,可以看到是怎样执行的,明天请假了,五一回去玩几天,哈哈,估计假期过来都忘了 import wx import sys class Frame(wx.Frame): def __init__(self,parent,id,title): print "Frame __init__" wx.Frame.__init__(self,parent,id,title) class App(wx.App): d

LINQ to SQL活学活用(4):监视你的一举一动

改进 这也可以运用GoF23中的观察者模式,让多个观察者对象(对象创建.修改)同时监听某一个主题对象(这里就是数据访问对象Customer).这个主题对象Customer在状态发生变化时,会通知所有观察者对象(对象创建.修改),使它们能够自动更新自己,就可以实现监视Customer的一举一动了. 我们来实现这个功能,先看看这篇完成的整体架构. 数据访问层 1.数据访问基类 我们为整个数据访问对象定义一个公共基类,首先定义定义数据访问基类接口,让抽象数据访问基类实现这个接口,这个基类用于存放数据访

LINQ to SQL活学活用(3):嗅出“臭味”烟消云散

改进 知道程序的"臭味",我们如何改进呢?想想,通过接口隐藏实体.我们利用接口实现,为Customer创建分部类,创建ICustomer接口,Customer实现ICustomer接口,利用ICustomer接口编写CustomerFacade,根据新的ICustomer接口更新单元测试,而不是上一节的对Customer对象做单元测试,这样客户就不知道数据访问层中具体实现了.这就是我们这一节做的工作. 数据访问层 进一步改进我们的程序,看看数据访问层的类图设计: 1.新建ICustom

LINQ to SQL活学活用(2):躲起来别让我看见

改进 这可以考虑到GoF23中的外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 考虑在数据访问层中建立外观Facade,这样可以为复杂的数据访问方法提供一个简单的类,使得耦合大大降低.增加外观Facade可以只向客户提供一个简单的接口,减少客户与数据访问层之间的依赖,更容易维护和扩展了. 数据访问层 我们就使用这种方法来改进第一篇的程序吧.首先创建一个基类用于存放公共的方法,然后各个数据访问对象(这里只有Custo

LINQ to SQL活学活用(1):这要打破旧观念

程序架构 现在比较经典的架构,看看下面图片. 如何实现 在一个N层应用程序中我们如何使用LINQ to SQL呢?这给刚刚入门的朋友的确是个难题,使用LINQ to SQL就是ORM技术,可以很轻松的实现对数据库记录增删查改操作,但是我们如何去"构建它"才更合理,更科学,更好用?这才是我们真正要学习的,使用面向对象的接口.抽象达到这个目的,面向接口编程就是更好的选择,可以更好的维护和测试. 下面一步一步完成这个程序吧,看到标题了吗?这篇是打破旧观念!看看接下来有什么神秘的地方.首先新建

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

Java内存结构: JVM内存结构主要是有三大块:堆内存.方法区和栈.堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分为三部分,Eden空间.From Survivor空间.To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配: 方法区存储类信息.常量.静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆): 栈又分为Java虚拟机栈和本地方法栈,主要用于方法的执行. 在通过一张图来了解如何通过参数来控制各区域的内存大

xhost命令详解(活学活用)

xhost命令是X服务器的访问控制工具,用来控制哪些X客户端能够在X服务器上显示.该命令必须从有显示连接的机器上运行.可以通过使用-host参数,从访问列表中除去一个名称.不要从访问列表中除去当前的名称.如果已经这样做了,请在作出任何更改之前注销系统. xhost 是用来控制X server访问权限的. 通常当你从hostA登陆到hostB上运行hostB上的应用程序时, 做为应用程序来说,hostA是client,但是作为图形来说, 是在hostA上显示的,需要使用hostA的Xserver,

活学活用 screen

新建桌面 screen 列出所有桌面 screen -ls 离开桌面 ctrl-A  D 进入之前的桌面 screen -RR 干掉当前桌面 ctrl-A  :quit 原文地址:https://www.cnblogs.com/yc2019/p/10459406.html