`
zhanshenny
  • 浏览: 264397 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JCaptcha+Memcache的验证码集群实现

 
阅读更多

一、问题背景

      为了防止垃圾信息发布机器人的自动提交攻击,采用CAPTCHA验证码来保护该模块,提高攻击者的成本。

 

二、验证码简介

       全自动区分计算机和人类的图灵测试(Completely Automated Public Turing test to tell Computers and Humans Apart,简称CAPTCHA)俗称验证码,是一种区分用户是计算机和人的公共全自动程序。在CAPTCHA测试中,作为服务器的计算机会自动生成一个问题由用户来解答。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。

       验证码作为一种辅助安全手段在Web安全中有着特殊的地位,验证码安全和web应用中的众多漏洞相比似乎微不足道,但是千里之堤毁于蚁穴,有些时候如果能绕过验证码,则可以把手动变为自动,对于Web安全检测有很大的帮助。大部分验证码的设计者都不知道为什么要用到验证码,或者对于如何检验验证码的强度没有任何概念。大多数验证码在实现的时候只是把文字印到背景稍微复杂点的图片上就完事了,程序员没有从根本上了解验证码的设计理念。

        JCaptcha即为Java版本的CAPTCHA项目,其是一个开源项目,支持生成图形和声音版的验证码,声音版的验证码需要使用到FreeTTS。
其原理是:服务器端首先随机产生一个字符串,生成一个图片,在后台存储一份。在做验证的时候,通过在后台传进去request参数获取到后台存储的值与输入的值进行比对。基于Servlet的使用方式可以参考官网的tutorial( https://jcaptcha.atlassian.net/wiki/display/general/5+minutes+application+integration+tutorial)

 

JCaptcha的架构图如下所示:
 

三、问题分析

       JCaptcha默认的实现是基于单机模式(MapCaptchaStore存储信息单机HashMap中),为了适应集群环境可以把验证信息存储在session中,但是要求Web服务器配置session stick或者Session复制。为了实现负载均衡且避免session复制带来的性能损失,集群部署方案是完全分布式的,既不是session stick也不进行session复制。进行验证时,由A节点到B节点进行验证,B节点CaptchaStore中store中得不到当前验证码,无法进行验证。  

        由上可知,如果把验证码统一存储在一个地方,问题将迎刃而解,故考虑自定义CaptchaStore采用memcache来存储,如下图所示:
        

 

四、具体实施

   1)在pom.xml加入依赖:       

<dependency>
    <groupId>com.octo.captcha</groupId>
    <artifactId>jcaptcha-all</artifactId>
    <version>1.0-RC6</version>
</dependency>

 
     2)JCaptcha与Spring集成配置
     applicationContext.xml:

<bean id="imageCaptchaService" class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService">
    <constructor-arg type="com.octo.captcha.service.captchastore.CaptchaStore" index="0">
        <ref bean="myCaptchaStore"/>
    </constructor-arg>
    <!--which captcha Engine you use-->
    <constructor-arg type="com.octo.captcha.engine.CaptchaEngine" index="1">
        <ref bean="myCaptchaEngine"/>
    </constructor-arg>
 
    <constructor-arg index="2">
        <value>180</value>
    </constructor-arg>
 
    <constructor-arg index="3">
        <value>100000</value>
    </constructor-arg>
 
    <constructor-arg index="4">
        <value>75000</value>
    </constructor-arg>
</bean>
 
<bean id="myCaptchaStore" class="com.xxx.util.MyCaptchaStore"/>
 
<!--you can define more than one captcha engine here -->
<bean id="myCaptchaEngine" class="com.xxx.util.MyCaptchaEngine"/>

 

        3)定制CaptchaStore

/**
 * 定制CaptchaStore
 * 线上集群环境,前端可能从A服务器取得验证码,而验证是到B服务器
 * 默认的hashmap store是保存在单个Jvm内存中的,这样验证就会有问题
 *
 */
public class MyCaptchaStore implements CaptchaStore {
    //CacheService负责封装memcache访问功能
    @Resource
    private CacheService cacheService;
  
    @Override
    public boolean hasCaptcha(String id) {
        CaptchaAndLocale captcha = cacheService.getCaptcha(id);
        return captcha == null false true;
    }
  
    @Override
    public void storeCaptcha(String id, Captcha captcha) throws CaptchaServiceException {
        try {
            cacheService.setCaptcha(id,new CaptchaAndLocale(captcha));
        catch (Exception e) {
            throw new CaptchaServiceException(e);
        }
    }
  
    @Override
    public void storeCaptcha(String id, Captcha captcha, Locale locale) throws CaptchaServiceException {
        try {
            cacheService.setCaptcha(id,new CaptchaAndLocale(captcha,locale));
        catch (Exception e) {
            throw new CaptchaServiceException(e);
        }
    }
  
    @Override
    public boolean removeCaptcha(String id) {
        return cacheService.removeCaptcha(id);
    }
  
    @Override
    public Captcha getCaptcha(String id) throws CaptchaServiceException {
        CaptchaAndLocale captchaAndLocale = cacheService.getCaptcha(id);
        return captchaAndLocale != null ? (captchaAndLocale.getCaptcha()) : null;
    }
  
    @Override
    public Locale getLocale(String id) throws CaptchaServiceException {
        CaptchaAndLocale captchaAndLocale = cacheService.getCaptcha(id);
        return captchaAndLocale != null ? (captchaAndLocale.getLocale()) : null;
    }
  
    @Override
    public int getSize() {
        return 0;
    }
  
    @Override
    public Collection getKeys() {
        return null;
    }
  
    @Override
    public void empty() {
    }
  
    @Override
    public void initAndStart() {
    }
  
    @Override
    public void cleanAndShutdown() {
    }
}

 

       4)定制验证码Engine

/**
 * 验证码Engine
 *
 */
public class MyCaptchaEngine extends ListImageCaptchaEngine {
  
    protected void buildInitialFactories() {
        int minWordLength = 4;
        int maxWordLength = 4;
        int fontSize = 20;
        int imageWidth = 100;
        int imageHeight = 30;
  
        WordGenerator wordGenerator = new RandomWordGenerator(
                "0123456789abcdefghijklmnopqrstuvwxyz");
  
        TextPaster randomPaster = new DecoratedRandomTextPaster(minWordLength,
                maxWordLength, new RandomListColorGenerator(new Color[] {
                new Color(2317027), new Color(2203411),
                new Color(2367172) }), new TextDecorator[] {});
        
        BackgroundGenerator background = new UniColorBackgroundGenerator(
                imageWidth, imageHeight, Color.LIGHT_GRAY);
        FontGenerator font = new RandomFontGenerator(fontSize, fontSize,
                new Font[] { new Font("nyala", Font.BOLD, fontSize),
                        new Font("Bell MT", Font.PLAIN, fontSize),
                        new Font("Credit valley", Font.BOLD, fontSize) });
  
        ImageDeformation postDef = new ImageDeformationByFilters(
                new ImageFilter[] {});
        ImageDeformation backDef = new ImageDeformationByFilters(
                new ImageFilter[] {});
        ImageDeformation textDef = new ImageDeformationByFilters(
                new ImageFilter[] {});
  
        WordToImage word2image = new DeformedComposedWordToImage(font,
                background, randomPaster, backDef, textDef, postDef);
        addFactory(new GimpyFactory(wordGenerator, word2image));
  
    }
}


         5)生成验证码图片

/**
 * 验证码
 */
@Controller
public class CaptchaController {
    private static final Logger LOGGER = LoggerFactory.getLogger(CaptchaController.class);
  
    @Resource
    private ImageCaptchaService imageCaptchaService;
  
    @RequestMapping(value = "/jcaptcha")
    public void ImageCaptcha(HttpServletRequest request , HttpServletResponse response) throws IOException {
        String captchaId = UUID.randomUUID().toString();
  
        BufferedImage image = imageCaptchaService.getImageChallengeForID(captchaId, request.getLocale());
        response.setHeader("Cache-Control""no-store");
        response.setHeader("Pragma""no-cache");
        response.setDateHeader("Expires"0);
        response.setContentType("image/jpeg");
  
        Cookie cookie = new Cookie(Constants.CAPTCHA_COOKIE_NAME,captchaId);
        cookie.setMaxAge(30*60);
        response.addCookie(cookie);
  
        ServletOutputStream responseOutputStream = response.getOutputStream();
        ImageIO.write(image, "jpg", responseOutputStream);
  
        try {
            responseOutputStream.flush();
        finally {
            responseOutputStream.close();
        }
  
    }
  
}

 

 
        6)验证过程

 

@RequestMapping(value = "/test",method = RequestMethod.POST)
@ResponseBody
public JsonResponse test( @RequestParam(value = "content") String content,
@RequestParam(value = "authCode") String authCode,
HttpServletRequest request) {
    String captchaId = null;
    Cookie[] cookies = request.getCookies();
    for (Cookie cookie : cookies) {
        if (cookie.getName().equals(Constants.CAPTCHA_COOKIE_NAME)) {
            captchaId = cookie.getValue();
            break;
        }
    }
 
    if (StringUtil.isBlank(captchaId)) {
        return new JsonResponse(40401,"验证码错误");
    }
 
    Boolean flag = false;
    try {
        flag = imageCaptchaService.validateResponseForID(captchaId, authCode);
    catch (CaptchaServiceException cse) {
 
    }
    if (!flag) {
        return new JsonResponse(40401,"验证码错误");
    }
 
    doSomething(); //业务任务
 
    return new JsonResponse(200,"success");
}
 
 

五、参考资料

分享到:
评论
1 楼 静夜独窗 2016-07-25  
请问,这段代码哪里用到了Memcache,整个验证用cookie实现的啊,集群没有用上,请指教

相关推荐

    tomcat+nginx+memcache集群

    tomcat+nginx+memcache高可用

    Nginx+tomcat6+memcache配置集群session共享所需jar包

    这里我们关注的是如何通过Nginx、Tomcat6和Memcached实现集群中的session共享。这个压缩包“Nginx+tomcat6+memcache所需jar包”提供了实现这一目标所需的组件。 首先,Nginx是一个高性能的反向代理服务器,常用于...

    Nginx+Memcache+Linux+Tomcat集群

    在构建高性能的Web服务环境中,...总结来说,Nginx+Memcache+Linux+Tomcat集群通过合理利用资源,实现了Web服务的高性能、高可用性和可扩展性。这种架构在处理大量并发请求时表现出色,是现代互联网服务的常见选择。

    Nginx+Memcache+Tomcat集群(session共享)

    总结来说,Nginx+Memcache+Tomcat集群的配置涉及到网络服务的部署、服务器间通信的配置以及session管理的实现。通过这种方式,可以提高网站的可用性和响应速度,同时降低了单点故障的风险。注意,实际操作时需根据...

    nginx+tomcat+memcache

    基于nginx+tomcat+memcache的负载均衡架构

    LNMP+Memcache

    CentOS Minimal LNMP + Memcache 编译安装过程 CentOS Minimal LNMP + Memcache 编译安装过程

    64位apache+mysql+php5.5.10+memcache

    标题 "64位apache+mysql+php5.5.10+memcache" 提供了我们正在处理一个针对64位Windows操作系统的软件套装,它包括Apache web服务器、MySQL数据库服务器、PHP 5.5.10脚本语言解释器以及Memcached缓存系统。...

    nginx+tomcat+memcache集群缓存配置及介质

    一 安装 1 jdk安装及tomcat7解压缩安装配置 不用说明 2 下载nginx1 4 2 for win32 解压安装 3 下载memcached服务端for win32 解压安装 设成windows服务端 执行memcached exe d install 4 下载tomcat7对应的memcached...

    Windows_Memcache安装(XAMPP+Memcache+PHP)

    面向对象的常用接口包括Memcache::connect(打开连接)、Memcache::pconnect(打开长连接)、Memcache::close(关闭连接)、Memcache::set(保存数据)、Memcache::get(提取数据)、Memcache::replace(替换数据)...

    apache+jk+memcache+nginx分布式网站建设笔记

    Memcached Session Manager是一个用于在Memcached集群中存储和管理session数据的插件。它依赖于Kryo序列化库、Spymemcached客户端等组件,以提供高效的数据序列化和网络传输能力。 #### 总结 构建基于Apache、...

    nginx+php+memcache

    在IT行业中,构建高效、可扩展的Web服务是至关重要的,而"nginx+php+memcache"的组合正是实现这一目标的有效方式。这个配置包专为Windows环境设计,旨在帮助用户快速搭建一个集成了静态资源处理、动态内容渲染以及...

    PHP实现多服务器session共享之memcache共享.rar

    1. **安装与配置memcache**:在所有服务器上安装memcache扩展,并确保它们都连接到同一个memcache服务集群。 2. **修改PHP配置**:在php.ini文件中,设置session存储handler为memcache。例如,添加以下行: ``` ...

    apache+tomcat+memcache

    【Apache+Tomcat+Memcache 集群部署加缓存】 Apache、Tomcat 和 Memcache 的组合可以创建一个高效且可扩展的Web应用程序环境,其中Apache作为前端负载均衡器,Tomcat作为Java应用服务器,而Memcache则作为缓存系统...

    Linux+nginx+php+mysql+memcache服务器安装,配置与优化.doc

    Linux+nginx+php+mysql+memcache服务器安装,配置与优化

    php + ajax + memcache 聊天室

    这个“php + ajax + memcache 聊天室”项目利用了PHP作为服务器端脚本语言,AJAX进行异步数据交换,以及Memcache作为缓存系统,以实现高效且流畅的聊天体验。以下将详细讲解这三个关键组成部分及其在聊天室中的应用...

    Linux+nginx+php+mysql+memcache

    - **原理**:基于键值对存储机制实现数据缓存。 - **应用场景**:网页缓存、用户会话数据缓存等。 #### 三、LNMPM环境搭建步骤 1. **环境准备**: - 操作系统选择:建议使用CentOS 7.x或更高版本。 - 安装必备...

    php-5.3.28+memcache

    在PHP中,通过Memcache扩展可以轻松地与Memcache服务器进行通信,实现数据的高速存取。 Memcache与PHP的结合,是Web应用性能优化的重要手段。当PHP脚本需要频繁访问数据库获取数据时,可以通过Memcache先检查数据...

    一键编译安装apache2.2.31+php5.3.29+memcache,适用于无法联网情况

    自动安装mysql,mysqlli,pdo_mysql,openssl,curl,gd,memcache等扩展。 chmod +x hj.sh ./hj.sh 如果提示:no such file or directory:说明hj.sh非unix编码重新编码 vi hj.sh :set ff=unix 按回车 按esc :wq 重新运行...

    Windows 2008R2+IIS7.5+PHP+Mysql+Wincache+Memcache+URL伪静态环境搭建教程

    在IIS 7.5环境下,可以通过安装和配置URL Rewrite模块来实现这一功能。 #### 第八步:Discuz优化 针对Discuz论坛系统进行特定的优化,包括但不限于调整数据库连接设置、缓存策略以及页面加载速度等方面的优化。 #...

    memcache+tomcat集群说明手册

    ### Memcache+Tomcat集群说明手册 #### 一、引言 随着互联网技术的发展与业务需求的不断增长,单一服务器已经难以满足高并发、大数据量处理的需求。因此,采用集群技术来提高系统的可用性、扩展性和性能变得尤为...

Global site tag (gtag.js) - Google Analytics