博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从构建分布式秒杀系统聊聊验证码
阅读量:7147 次
发布时间:2019-06-29

本文共 5312 字,大约阅读时间需要 17 分钟。

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

前言

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

验证码

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

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

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

场景

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

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

  • 登录注册,为你防护撞库×××、阻止注册机批量注册
  • 活动秒杀,有效拦截刷单操作,让羊毛党空手而归
  • 点赞发帖,有效解决广告屠版、恶意灌水、刷票问题
  • 数据保护,防止自动机、爬虫盗取网页内容和数据

申请

申请地址:

在线体验:

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

  • 2000次/小时安全防护
  • 支持免验证+分级验证
  • 三分钟快速接入
  • 全功能配置后台
  • 支持HTTPS
  • 阈值内流量无广告

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

接入

快读接入:

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

前端

引入JS:

页面元素:

JS回调:

后端

@Api(tags = "秒杀商品")@RestController@RequestMapping("/seckillPage")public class SeckillPageController {    @Autowired    private ActiveMQSender activeMQSender;    //自定义工具类    @Autowired    private HttpClient httpClient;    //这里自行配置参数    @Value("${qq.captcha.url}")    private String url;    @Value("${qq.captcha.aid}")    private String aid;    @Value("${qq.captcha.AppSecretKey}")    private String appSecretKey;    @RequestMapping("/startSeckill")    public Result  startSeckill(String ticket,String randstr,HttpServletRequest request) {        HttpMethod method =HttpMethod.POST;        MultiValueMap
params= new LinkedMultiValueMap
(); params.add("aid", aid); params.add("AppSecretKey", appSecretKey); params.add("Ticket", ticket); params.add("Randstr", randstr); params.add("UserIP", IPUtils.getIpAddr(request)); String msg = 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); String response = (String) json.get("response"); if("1".equals(response)){ //进入队列、假数据而已 Destination destination = new ActiveMQQueue("seckill.queue"); activeMQSender.sendChannelMess(destination,1000+";"+1); return Result.ok(); }else{ return Result.error("验证失败"); } }}

自定义请求工具类 HttpClient:

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

获取IP地址工具类 IPUtils :

/** * IP地址 */public class 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 static String 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(","));            }        }        return ip;    }}

案例效果图

启动项目访问:

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

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

定制接入

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

登录
login: function () {    //这里校验用户名以及密码    // 直接生成一个验证码对象    var captcha = new TencentCaptcha('2001344788', function(res) {        if(res.ret === 0){//回调成功            var data = {'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(); // 显示验证码},

后台监控

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

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

小结

总体来说,系统接入人机验证码还是很方便的,并没有技术难点,难点已经被提供商封装,我们只需要简单的调用即可。

秒杀案例:

演示案例(点击生成按钮):

转载于:https://blog.51cto.com/itstyle/2286975

你可能感兴趣的文章
Unity组件:Audio Chorus Filter PRO only 音频合声滤波器 ...
查看>>
Flutter MergeableMaterialItem
查看>>
开源监控利器Prometheus初探
查看>>
性能优化技巧 - 查找
查看>>
在阿里,我们如何管理测试环境
查看>>
Kubernetes 实战教学,手把手教您如何在 K8s 平台上使用 Compose(一) ...
查看>>
MultipartFile文件上传
查看>>
如何不花钱得到一辆特斯拉Model 3,挖漏就成
查看>>
python设计模式(六):桥接模式
查看>>
手写Java线程池
查看>>
编排管理成容器云关键 Kubernetes(K8s)和Swarm对比分析 ...
查看>>
Spring Cloud Hystrix源码分析
查看>>
AMD的双向田忌赛马:7nm中端CPU挑落顶级i9,旗舰GPU只对位NV三当家 | CES 2019 ...
查看>>
云服务器ECS出现速度变慢 以及突然断开怎么办? ...
查看>>
“水泊梁山“互联网有限公司一百单八将内部社交网络
查看>>
国内最大母婴社区宝宝树创始人邵亦波辞任公司董事,将专注慈善事业
查看>>
一入爬虫深似海,总结python爬虫学习笔记!
查看>>
Python零基础学习笔记(十九)—— 死循环
查看>>
「镁客·请讲」诺亦腾刘昊扬:VR教育了市场,但动作捕捉技术的契机绝不止于VR...
查看>>
IoT与区块链的机遇与挑战
查看>>