从构建分布式秒杀系统聊聊验证码

前言

为了拦截大部分请求,秒杀案例前端引入了验证码。淘宝上很多人吐槽,等输入完秒杀活动结束了,对,结束了...... 当然了,验证码的真正作用是,有效拦截刷单操作,让羊毛党空手而归。

验证码

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

那么到底什么是验证码呢?验证码作为一种人机识别手段,其终极目的,就是区分正常人和机器的操作。我们常见的互联网注册、登录、发帖、领优惠券、投票等等应用场景,都有被机器刷造成各类损失的风险。

目前常见的验证码形式多为图片验证码,即数字、字母、文字、图片物体等形式的传统字符验证码。这类验证码看似简单易操作,但实际用户体验较差(参见12306网站),且随着OCR技术和打码平台的利用,图片比较容易被破解,被破解之后就形同虚设。

这里我们使用腾讯的智能人机安全验证码,告别传统验证码的单点防御,十道安全栅栏打造立体全面的安全验证,将黑产拒之门外。

场景

下面我们来瞅瞅验证码轻松解决了那些场景安全问题:

登录注册,为你防护撞库攻击、阻止注册机批量注册

活动秒杀,有效拦截刷单操作,让羊毛党空手而归

点赞发帖,有效解决广告屠版、恶意灌水、刷票问题

数据保护,防止自动机、爬虫盗取网页内容和数据

申请

申请地址:https://007.qq.com/product.html

在线体验:https://007.qq.com/online.html

只要一个QQ就可以免费申请,对于一般的企业OA系统或者个人博客网站,验证码免费套餐足够了已经,具备以下特点:

2000次/小时安全防护

支持免验证+分级验证

三分钟快速接入

全功能配置后台

支持HTTPS

阈值内流量无广告

2000次/小时的安全防护,一般很少达到如此效果,当然了即时超出阈值,顶多也就是多个广告而已。

接入

快读接入:https://007.qq.com/quick-start.html

接入与帮助提供了多种客户端和服务端的接入案例,这里我们使用我们秒杀案例中最熟悉的Java语言来接入。

前端

引入JS:

页面元素:

<!--点击此元素会自动激活验证码,不一定是button,其他标签也可以--><!--id : 元素的id(必须)--><!--data-appid : AppID(必须)--><!--data-cbfn : 回调函数名(必须)--><!--data-biz-state : 业务自定义透传参数(可选)-->验证

JS回调:

window.callback =function(res){console.log(res)// res(未通过验证)= {ret: 1, ticket: null}// res(验证成功) = {ret: 0, ticket: "String", randstr: "String"}if(res.ret ===0){ startSeckill(res) } }//后台验证ticket,并进入秒杀队列functionstartSeckill(res){ $.ajax({url:"startSeckill",type:'post',data: {'ticket': res.ticket,'randstr':res.randstr},success:function(result){//验证是否通过,提示用户} }); }

后端

@Api(tags ="秒杀商品")@RestController@RequestMapping("/seckillPage")publicclassSeckillPageController {@AutowiredprivateActiveMQSender activeMQSender;//自定义工具类@AutowiredprivateHttpClient httpClient;//这里自行配置参数@Value("${qq.captcha.url}")privateStringurl;@Value("${qq.captcha.aid}")privateStringaid;@Value("${qq.captcha.AppSecretKey}")privateStringappSecretKey;@RequestMapping("/startSeckill")publicResult startSeckill(Stringticket,Stringrandstr,HttpServletRequest request) { HttpMethod method =HttpMethod.POST; MultiValueMap params=newLinkedMultiValueMap(); params.add("aid", aid); params.add("AppSecretKey", appSecretKey); params.add("Ticket", ticket); params.add("Randstr", randstr); params.add("UserIP", IPUtils.getIpAddr(request));Stringmsg = httpClient.client(url,method,params);/**

* response: 1:验证成功,0:验证失败,100:AppSecretKey参数校验错误[required]

* evil_level:[0,100],恶意等级[optional]

* err_msg:验证错误信息[optional]

*///{"response":"1","evil_level":"0","err_msg":"OK"}JSONObject json = JSONObject.parseObject(msg);Stringresponse = (String) json.get("response");if("1".equals(response)){//进入队列、假数据而已Destination destination =newActiveMQQueue("seckill.queue"); activeMQSender.sendChannelMess(destination,1000+";"+1);returnResult.ok(); }else{returnResult.error("验证失败"); } }}

自定义请求工具类 HttpClient:

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

@ServicepublicclassHttpClient{ publicStringclient(Stringurl, HttpMethod method, MultiValueMap params){ RestTemplate client =newRestTemplate(); HttpHeaders headers =newHttpHeaders();// 请勿轻易改变此提交方式,大部分的情况下,提交方式都是表单提交headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); HttpEntity> requestEntity =newHttpEntity>(params, headers);// 执行HTTP请求ResponseEntity response = client.exchange(url, HttpMethod.POST, requestEntity,String.class);returnresponse.getBody(); }}

获取IP地址工具类 IPUtils :

/**

* IP地址

*/publicclass IPUtils { private static Logger logger = LoggerFactory.getLogger(IPUtils.class);/**

* 获取IP地址

* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址

* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址

*/public staticString getIpAddr(HttpServletRequest request) {String ip= null;try {ip= request.getHeader("x-forwarded-for");if(StringUtils.isEmpty(ip) ||"unknown".equalsIgnoreCase(ip)) {ip= request.getHeader("Proxy-Client-IP");}if(StringUtils.isEmpty(ip) ||ip.length() ==0||"unknown".equalsIgnoreCase(ip)) {ip= request.getHeader("WL-Proxy-Client-IP");}if(StringUtils.isEmpty(ip) ||"unknown".equalsIgnoreCase(ip)) {ip= request.getHeader("HTTP_CLIENT_IP");}if(StringUtils.isEmpty(ip) ||"unknown".equalsIgnoreCase(ip)) {ip= request.getHeader("HTTP_X_FORWARDED_FOR");}if(StringUtils.isEmpty(ip) ||"unknown".equalsIgnoreCase(ip)) {ip= request.getRemoteAddr();} } catch (Exception e) { logger.error("IPUtils ERROR ", e);} // 使用代理,则获取第一个IP地址if(StringUtils.isEmpty(ip) &&ip.length() >15) {if(ip.indexOf(",") >0) {ip=ip.substring(0, ip.indexOf(","));} } returnip;}}

案例效果图

启动项目访问:http://localhost:8080/seckill/1000.shtml

定制接入

在系统登录的时候,我们需要先校验用户名以及密码,然后调用验证码操作,这里就需要我们定制接入了。

登录login:function(){//这里校验用户名以及密码// 直接生成一个验证码对象varcaptcha =newTencentCaptcha('2001344788',function(res){if(res.ret ===0){//回调成功vardata = {'username':username,'password':password,'ticket':res.ticket,'randstr':res.randstr} $.ajax({type:"POST",url:"sys/loginCaptcha",data: data,dataType:"json",success:function(result){//校验是否成功} }); } }); captcha.show();// 显示验证码},

后台监控

腾讯后台还提供了简单实用的数据监控,如下:

欢迎工作一到八年的Java工程师朋友们加入Java高级交流:854630135

本群提供免费的学习指导 架构资料 以及免费的解答

不懂得问题都可以在本群提出来 之后还会有直播平台和讲师直接交流噢

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容