您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

c#实现简单Token口令验证

时间:2020-07-22 13:08:13  来源:  作者:

最近一个项目发现手机验证码总是被人盗刷,一秒钟刷了1百多个,很明显这种行为是通过软件自动提交的,自动发帖机原理类似,解决这个问题目前有两个方案。

出现这个问题原因:请求手机验证码Api时没有任何带任何验证,只要请求了手机号正确就执行发送操作,软件或代码很容易伪造请求过程。

解决方案有很多种,可以选择下面一种或几种组合起来使用。

方案1:用户获取手机验证码时候弹出图片验证码,输入后再发送。

优点:增加伪造请求成功的难度,必须输入验证码才可以发送,如果是软件,软件需要有图片验证码识别功能。

缺点:用户体验不好,增加了普通用户的操作步骤。

方案2:增加ip黑名单,即检测请求ip的发送频率,如同一个ip一分钟内请求多少次后屏蔽ip。

缺点:一些软件有主动更换ip的功能,这种方式效果不是很好。

方案3:加上token口令验证

在Api中增加口令验证,即每次请求必须带上token,token验证正确了才执行发送操作。

这个方案为了替代方案1,因为有的公司对前端体验要求极高,增加用户操作步骤后用户体验不好。

这里token可以放在数据库中,也可以放在内存中,个人建议放在内存中,速度快,查询快,至于冗余的问题,可以增加一个BuildDate来保存生成时间。

Token描述类

   public class TokenDescriptor
    {
        /// <summary>
        /// 客户端唯一Id,必须保证唯一性
        /// </summary>
        public string ClientId { get; set; }
        /// <summary>
        /// token
        /// </summary>

        public string Token { get; set; }

        /// <summary>
        /// token生成日期
        /// </summary>
        public DateTime BuildDate { get; set; }
    }

token处理类

  public class TokenFactoryBLL
    {
        private string _ClientId;
        private static List<TokenDescriptor> _TokenList;

        static TokenFactoryBLL()
        {
            _TokenList = new List<TokenDescriptor>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="httpRequestBase"></param>
        public TokenFactoryBLL(HttpRequestBase httpRequestBase)
        {
            _ClientId = StringHelper.ClientId(httpRequestBase);
            ClearExpired();
        }

        /// <summary>
        /// 可用于远程api
        /// </summary>
        /// <param name="clientId"></param>
        public TokenFactoryBLL(string clientId)
        {
            _ClientId = clientId;
            ClearExpired();
        }

        /// <summary>
        /// 生成口令
        /// </summary>
        public string Get()
        {
            if (string.IsNullOrEmpty(_ClientId))
            {
                return null;
            }
            string token = Guid.NewGuid().ToString("N");//guid;
            TokenDescriptor tokenDescriptor = new TokenDescriptor();
            tokenDescriptor.ClientId = _ClientId;
            tokenDescriptor.Token = StringHelper.NewGuid();
            tokenDescriptor.BuildDate = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            _TokenList.Add(tokenDescriptor);
            return token;
        }

        /// <summary>
        /// 验证token
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public TipsInfo Validate(string token)
        {
            TipsInfo tipsInfo = new TipsInfo();
            if (!_TokenList.Exists(c => c.ClientId.Equals(_ClientId, StringComparison.OrdinalIgnoreCase) && c.Token.Equals(token, StringComparison.OrdinalIgnoreCase)))
            {
                tipsInfo.State = 0;
                tipsInfo.Msg = "token口令验证失败";
            }
            return tipsInfo;
        }

        /// <summary>
        /// 移除对应客户端的所有token
        /// </summary>
        /// <param name="clientId"></param>
        public void Remove()
        {
            _TokenList.RemoveAll(c => c.ClientId.Equals(_ClientId, StringComparison.OrdinalIgnoreCase));
        }


        /// <summary>
        /// 清理过期的token,过期时间10分钟
        /// </summary>
        private static void ClearExpired()
        {
            for (var i = 0; i < _TokenList.Count; i++)
            {
                TokenDescriptor item = _TokenList[i];
                DateTime startTime = item.BuildDate;
                DateTime endTime = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                TimeSpan ts = endTime - startTime;
                int minutes = ts.Minutes;
                if (minutes>10)
                {
                    _TokenList.RemoveAt(i);
                }
            }
        }
    }

附上Helper工具类中clientId的方法:

        /// <summary>
        /// 获取并生成客户端唯一Id,保存在cookie中;
        /// </summary>
        /// <returns></returns>
        public static string ClientId(HttpRequestBase request) //获取客户端唯一Id
        {
            if (request == null)
            {
                return null;
            }
            string clientId = CookieHelper.Get("_clientId_");
            if (string.IsNullOrEmpty(clientId))
            {
                string guid = Guid.NewGuid().ToString("N");//guid
                string ip = StringHelper.GetClientIP().ToString().Replace(".", "").Replace(":", "");
                clientId = guid + ip;
                CookieHelper.Add("_clientId_", clientId);
            }
            return clientId;
        }

        /// <summary>
        /// 获取客户端ip
        /// </summary>
        /// <returns></returns>
        public static string GetClientIP()
        {
            if (System.Web.HttpContext.Current == null) return "127.0.0.1";
            string clientIp = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            if (string.IsNullOrEmpty(clientIp))
            {
                clientIp = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
            }
            if (string.IsNullOrEmpty(clientIp))
            {
                clientIp = System.Web.HttpContext.Current.Request.UserHostAddress;
            }
            if (clientIp.IndexOf(",") > 0)
            {
                clientIp = clientIp.Split(',')[0];
            }
            if (!StringHelper.IsIp(clientIp))
            {
                clientIp = "127.0.0.1";
            }
            return clientIp;
        }

前端发送方式需要更改,在请求手机验证码的api之前,先第一次请求获取token,然后带上token验证通过后后再请求api。

这个方案肯定没有验证码的防护效果好,只是增加了伪造请求的步骤,因为现在很多模拟请求的软件都是一次性请求,所以还是能防护大部分的软件。



Tags:C# Token   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
最近一个项目发现手机验证码总是被人盗刷,一秒钟刷了1百多个,很明显这种行为是通过软件自动提交的,自动发帖机原理类似,解决这个问题目前有两个方案。出现这个问题原因:请求手机...【详细内容】
2020-07-22  Tags: C# Token  点击:(78)  评论:(0)  加入收藏
▌简易百科推荐
一、简介很多时候我们都需要用到一些验证的方法,有时候需要用正则表达式校验数据时,往往需要到网上找很久,结果找到的还不是很符合自己想要的。所以我把自己整理的校验帮助类分...【详细内容】
2021-12-27  中年农码工    Tags:C#   点击:(2)  评论:(0)  加入收藏
引言在学习C语言或者其他编程语言的时候,我们编写的一个程序代码,基本都是在屏幕上打印出 hello world ,开始步入编程世(深)界(坑)的。C 语言版本的 hello world 代码:#include <std...【详细内容】
2021-12-21  一起学嵌入式    Tags:C 语言   点击:(11)  评论:(0)  加入收藏
读取SQLite数据库,就是读取一个路径\\192.168.100.**\position\db.sqlite下的文件<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0"/...【详细内容】
2021-12-16  今朝我的奋斗    Tags:c#   点击:(21)  评论:(0)  加入收藏
什么是shellshell是c语言编写的程序,它在用户和操作系统之间架起了一座桥梁,用户可以通过这个桥梁访问操作系统内核服务。 它既是一种命令语言,同时也是一种程序设计语言,你可以...【详细内容】
2021-12-16  梦回故里归来    Tags:shell脚本   点击:(18)  评论:(0)  加入收藏
一、编程语言1.根据熟悉的语言,谈谈两种语言的区别?主要浅谈下C/C++和PHP语言的区别:1)PHP弱类型语言,一种脚本语言,对数据的类型不要求过多,较多的应用于Web应用开发,现在好多互...【详细内容】
2021-12-15  linux上的码农    Tags:c/c++   点击:(17)  评论:(0)  加入收藏
1.字符串数组+初始化char s1[]="array"; //字符数组char s2[6]="array"; //数组长度=字符串长度+1,因为字符串末尾会自动添&lsquo;\0&lsquo;printf("%s,%c\n",s1,s2[2]);...【详细内容】
2021-12-08  灯-灯灯    Tags:C语言   点击:(47)  评论:(0)  加入收藏
函数调用约定(Calling Convention),是一个重要的基础概念,用来规定调用者和被调用者是如何传递参数的,既调用者如何将参数按照什么样的规范传递给被调用者。在参数传递中,有两个很...【详细内容】
2021-11-30  小智雅汇    Tags:函数   点击:(19)  评论:(0)  加入收藏
一、问题提出问题:把m个苹果放入n个盘子中,允许有的盘子为空,共有多少种方法?注:5,1,1和1 5 1属同一种方法m,n均小于10二、算法分析设f(m,n) 为m个苹果,n个盘子的放法数目,则先对...【详细内容】
2021-11-17  C语言编程    Tags:C语言   点击:(49)  评论:(0)  加入收藏
一、为什么需要使用内存池在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗;另一...【详细内容】
2021-11-17  深度Linux    Tags:C++   点击:(38)  评论:(0)  加入收藏
OpenCV(Open Source Computer Vision Library)是一个(开源免费)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android、ios等操作系统上,它轻量级而且高效---由一系列...【详细内容】
2021-11-11  zls315    Tags:C#   点击:(50)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条