zoukankan      html  css  js  c++  java
  • 从12306谈起验证码的架构

    最近和众屌丝一样,在12306上面刷着春节回家的票。与她大战无数个回合之后,终于抢到了一张回家的高铁票,不断感慨最近人品还不错。当前,在使用12306的过程中,充满很多的心酸,念叨了铁道部的亲人很多次(罪过),其中最让人纠结的一项即是:验证码。
         12306采用验证码, 无疑是一种很不错的措施,可以在一定程度上阻止了黄牛们的疯狂行为,不过也给正常使用验证码的童鞋带了个很头痛的问题,在选座提交订单的关键时候,竟然验证码图片拉取不下来又或者验证过程非常耗时。鉴于自己也是无数码农中的有这职业病的一员,为此也来谈谈关于验证码的优化方案。
          验证码通常一张静态的图片,但是12306使用的却是一张动态的图片(GIF格式),动态图片的验证码大大的提高了破解的难度,但无疑也具备比较高的生成成本。我们首先来看一下通常情况下的验证码校验流程:


         1)浏览器向验证码服务器发起获取验证码图片的请求;
         2)验证码服务器返回验证码图片文件和图片文件对应的ID编号(ID编号一般下发到浏览器的Cookie中);
         3)浏览器提交用户输入的验证码和图片文件ID编号;
         4)Web服务器校验用户输入的验证码与图片中展示的验证码是否一致,根据校验结果来向用户展示不同的页面;

         下面我们来看一下系统的整体架构图:

        主要处理流程为:
        1、获取验证码
             1)从配置中心中获取当前可用的频率控制、验证码库、待验证库可用的服务地址列表
             2)频率控制,主要控制当前请求是否属于恶意攻击;
             3)验证码库,获取可用的验证码信息,即获取一个可用的验证码图片文件地址;
             4)将获取到的验证码信息写入到待验证库中,方便后续进行校验验证码;
         2、校验验证码
             1)根据从前端获取到的图片编号(获取验证码时,下发的对应编号),来从待验证库中获取验证信息;
             2)将验证结果发送到校验统计模块;

        在设计过程中需要考虑的点:
        1)恶意刷新
         来自某个IP频繁的请求验证码,大体上就可以判定验证码正在被刷中,需要采取措施进行一定的频率限制,降低继续被刷的请求量,这里我们可以采用比较简单限制来个某个IP请求次数,当然也可以根据业务特性,添加业务对应的频率控制逻辑;
         2)验证码有效期
         验证码可以生成一条Key—Value的数据存放到Memcache中(即待验证库),Value为:验证码图片文件ID、验证码Code、生成时间,每个请求验证码图片的请求,均在缓存中插入一条记录,每发送一个验证请求,即将缓存中的这条记录删除失效。
         3)图片文件与图片编号
         两者之间不可以建立一一对应的关系,否则,坏人只需要保存图片编号,下次就可以重复提交对应的验证码;但是可以建立一对多的关系,也就是一张图片对应多个图片编号,但是一图片编号只能唯一对应图片;
          4)验证码生成
         验证码图片文件的生成相比而言是比较慢的操作,所以需要采用离线计算成功的方案,避免浏览器获取图片验证码文件耗时比较久,影响用户的体验;这里存放一个问题,什么时机启动验证码生成的流程,有以下几种策略:
           a)每天定时生成更新,策略简单实现;
           b)定时检测验证码可用量,来生成一批新的验证码文件;
         总之,验证码图片文件的生成必须是离线进行的,同时在此时需要为图片文件生成一个或者多个编号;服务部署的个数,完全取决于验证码的消耗量;
         5)验证码库
         验证码库,应该采用那种数据结构?Mysql还是其他的数据结构。在这里,可以尝试采用Redis的list结构来当作消息队列来使用或者其他的可用的消息队列。需要获取验证码时,从消息队列中Pop出一个值即可。每个记录中至少需要存储的字段为:图片编号、图片地址、验证码等信息。消息队列中的记录 < 50%时,可以出发验证码生成逻辑来定时插入新的验证码。面对更并发的验证码请求量,可以在集群中多部署几套Redis消息队列以及验证码生成系统来应对;
         6)图片编号的生成
         验证码图片文件需要支持在多台机器上面部署多个服务,同时也要支持在单机上面部署多个服务,这里就需要解决如何来生成唯一验证码图片编号的问题。可以通过根据机器IP和服务编号来解决这个问题,每台机器上面的服务均存在一个服务编号(保证服务编号在单机上是唯一的),计算公式可以为:ID = MD5(IP+服务编号+时间戳+自动序列号);

    其实,上面写的都是错的,我压根没有做过类似的服务。
  • 相关阅读:
    java 通过Iterator输出Map
    java SortedSet接口swap方法
    java vector
    java Iterator双向迭代输出
    java 序列化和反序列化多个对象
    培训
    每天晚上

    又想起了

  • 原文地址:https://www.cnblogs.com/mtcnn/p/9410099.html
Copyright © 2011-2022 走看看