zoukankan      html  css  js  c++  java
  • 活学活用,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:cloudserviceWebAPIServiceMiddlewareServiceSvr.cs:行号 97
    ---> (内部异常 #0) System.Net.Http.HttpRequestException: 响应状态代码不指示成功: 401 (Unauthorized)。
    

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

        

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

    这个思路主要是这样的:

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

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

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

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

  • 相关阅读:
    android cocos2d-x视频
    Android OpenGL 学习笔记 --开始篇
    Nginx配置详解
    扩展RBAC用户角色权限设计方案
    几种序列化与Get、Set方法的关系
    Kettle大量数据快速导出的解决方案(利用SQL导出百万级数据,挺快的)
    SpringBoot 标准集成MyBatis的2种方式
    Apache Commons io FileUtils 详解
    SpringBoot在工具类中读取配置文件(ClassPathResource)
    利用guava封装RateLimiter 令牌桶算法(AOP实现)
  • 原文地址:https://www.cnblogs.com/datacool/p/datacool_http_basic_auth.html
Copyright © 2011-2022 走看看