最近一个项目发现手机验证码总是被人盗刷,一秒钟刷了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");//guidstring 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;}
推荐阅读
- Masonry 纯CSS实现瀑布流
- Alibaba开源Java诊断工具Arthas简单介绍
- Excel提取不重复员工姓名,就这么简单
- PHP位运算和逻辑运算,一个符号写两遍这么简单?
- Python实现信息自动配对爬虫排版程序
- 甘道芯茶的功效,简单介绍眉茶的品种分类
- 祁门工夫红茶的泡法[红茶]
- 纯CSS实现等高价目表
- 使用Java带你打造一款简单的外卖系统
- 关于普洱茶的茶号,简单的出乎你意料
