关于jCaptcha验证码插件的使用

   记录一下使用jCaptcha生成图形验证码的过程。

首先,使用jCaptcha需要导入其jar包,这里我的项目是maven项目,我就直接在pom里面导入了。


pom代码:

<dependency>
<groupId>com.octo.captcha</groupId>
<artifactId>jcaptcha-all</artifactId>
<version>1.0-RC6</version>
<exclusions>
<exclusion>
<groupId>quartz</groupId>
<artifactId>quartz</artifactId>
</exclusion>
<exclusion>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</exclusion>
<exclusion>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
</exclusion>
<exclusion>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</exclusion>
<exclusion>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</exclusion>
<exclusion>
<groupId>concurrent</groupId>
<artifactId>concurrent</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</exclusion>
<exclusion>
<groupId>xerces</groupId>
<artifactId>xmlParserAPIs</artifactId>
</exclusion>
</exclusions>
</dependency>


ok,jar包导入后开始配置spring,注入相关bean。包含了jcaptcha使用相关的service、文字产生器、字体、颜色、背景等等,以下为相关配置。这里关于验证码工厂我根据自身需求,重写了jCaptcha的GimpyFactory类和Gimpy类。这样验证码匹配的时候就可以实现忽略大小写了。后面我会详细说明一下如何重写这两个类。

spring配置:

<!-- 验证码服务 -->
<bean id="captchaService" class="com.octo.captcha.service.multitype.GenericManageableCaptchaService">
<constructor-arg index="0" ref="imageEngine" />
<constructor-arg type="int" index="1" value="180" /><!--超时时间 秒-->
<constructor-arg type="int" index="2" value="100000" /><!--最大并发数-->
</bean>


<!-- 图片引擎 -->
<bean id="imageEngine" class="com.octo.captcha.engine.GenericCaptchaEngine">
<constructor-arg index="0">
<list>
<ref bean="captchaFactory" />
</list>
</constructor-arg>
</bean>


<!-- 验证码工厂(此类为重写captcha内GimpyFactory类,为了实现验证码不区分大小写) -->
<bean id="captchaFactory" class="com.cpic.jvglapp.claimserver.entity.captcha.GimpyFactoryOverwrite">
<constructor-arg>
<ref bean="wordgen" />
</constructor-arg>
<constructor-arg>
<ref bean="wordtoimage" />
</constructor-arg>
</bean>


<!-- 文字产生器 -->
<bean id="wordgen" class="com.octo.captcha.component.word.wordgenerator.RandomWordGenerator">
<!--可选字符-->
<constructor-arg>
<value>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ</value>
</constructor-arg>
</bean>


<!-- 图片生成器 -->
<bean id="wordtoimage" class="com.octo.captcha.component.image.wordtoimage.ComposedWordToImage">
<constructor-arg index="0">
<ref bean="fontGenRandom" />
</constructor-arg>
<constructor-arg index="1">
<ref bean="backGenUni" />
</constructor-arg>
<constructor-arg index="2">
<ref bean="decoratedPaster" />
</constructor-arg>
</bean>


<!-- 文字转换图片 -->
<bean id="fontGenRandom" class="com.octo.captcha.component.image.fontgenerator.RandomFontGenerator">
<!--最小字体-->
<constructor-arg index="0">
<value>20</value>
</constructor-arg>
<!--最大字体-->
<constructor-arg index="1">
<value>20</value>
</constructor-arg>
<constructor-arg index="2">
<list>
<bean class="java.awt.Font">
<constructor-arg index="0">
<value>Arial</value>
</constructor-arg>
<constructor-arg index="1">
<value>0</value>
</constructor-arg>
<constructor-arg index="2">
<value>20</value>
</constructor-arg>
</bean>
</list>
</constructor-arg>
</bean>



<bean id="backGenUni" class="com.octo.captcha.component.image.backgroundgenerator.UniColorBackgroundGenerator">
<!--背景宽度-->
<constructor-arg index="0">
<value>80</value>
</constructor-arg>
<!--背景高度-->
<constructor-arg index="1">
<value>32</value>
</constructor-arg>
</bean>


<bean id="decoratedPaster" class="com.octo.captcha.component.image.textpaster.DecoratedRandomTextPaster">
<!--最大字符长度-->
<constructor-arg type="java.lang.Integer" index="0">
<value>4</value>
</constructor-arg>
<!--最小字符长度-->
<constructor-arg type="java.lang.Integer" index="1">
<value>4</value>
</constructor-arg>
<!--文本颜色-->
<constructor-arg index="2">
<ref bean="colorGen" />
</constructor-arg>
<!--文本混淆-->
<constructor-arg index="3">
<list>
<!--<ref bean="baffleDecorator"/>-->
</list>
</constructor-arg>
</bean>


<bean id="baffleDecorator" class="com.octo.captcha.component.image.textpaster.textdecorator.BaffleTextDecorator">
<constructor-arg type="java.lang.Integer" index="0">
<value>1</value>
</constructor-arg>
<constructor-arg type="java.awt.Color" index="1">
<ref bean="colorWrite" />
</constructor-arg>
</bean>


<bean id="colorGen" class="com.octo.captcha.component.image.color.SingleColorGenerator">
<constructor-arg type="java.awt.Color" index="0">
<ref bean="colorDimGrey" />
</constructor-arg>
</bean>


<bean id="colorWrite" class="java.awt.Color">
<constructor-arg type="int" index="0">
<value>255</value>
</constructor-arg>
<constructor-arg type="int" index="1">
<value>255</value>
</constructor-arg>
<constructor-arg type="int" index="2">
<value>255</value>
</constructor-arg>
</bean>


<bean id="colorDimGrey" class="java.awt.Color">
<constructor-arg type="int" index="0">
<value>105</value>
</constructor-arg>
<constructor-arg type="int" index="1">
<value>105</value>
</constructor-arg>
<constructor-arg type="int" index="2">
<value>105</value>
</constructor-arg>
</bean>


然后,在实现类里注入jCaptcha的服务类,调用其提供的图片生成函数和校验函数就搞定了。

java代码(生成验证码):

@RequestMapping("/captcha")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
try {
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
String captchaId = request.getSession().getId();
BufferedImage challenge = imageCaptchaService.getImageChallengeForID(captchaId, request.getLocale());


response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0L);
response.setContentType("image/jpeg");

ImageIO.write(challenge, "jpeg", jpegOutputStream);
byte[] captchaChallengeAsJpeg = jpegOutputStream.toByteArray();


ServletOutputStream respOs = response.getOutputStream();
respOs.write(captchaChallengeAsJpeg);
respOs.flush();
respOs.close();
} catch (IOException e) {
logger.error("generate captcha image error: {}", e.getMessage());
}
}

     这里返回的是一张图片,前端<img href="#" src="xxxxxxx/captcha.do"/>即可获取到验证码图片。

     java代码(校验验证码):

captchaFlag = imageCaptchaService.validateResponseForID(session.getId(), captcha);//校验验证码正确性

     一行代码就搞定了,但是需要注意的是,jCaptcha自身提供的验证码校验是区分大小写的。

    接来下,说明一下如何实现验证码不区分大小吧。

我们先来看一下jCaptcha的源码是怎么校验的,先看下代码:

public Boolean validateResponseForID(String ID, Object response)
    throws CaptchaServiceException
  {
    if (!this.store.hasCaptcha(ID)) {
      throw new CaptchaServiceException("Invalid ID, could not validate unexisting or already validated captcha");
    }
    Boolean valid = this.store.getCaptcha(ID).validateResponse(response);
    this.store.removeCaptcha(ID);
    return valid;
  }

这是validateResponseForID的代码,this.store.hasCaptcha是判断该id下是否已经生成验证码,没有生成会抛出异常需要注意。然后校验是在this.store.getCaptcha(ID).validateResponse(reponse);接下来看看他是如何校验的。

看看代码:

public final Boolean validateResponse(Object response)
  {
    return (null != response) && ((response instanceof String)) ? validateResponse((String)response) : Boolean.FALSE;
  }
  
  private final Boolean validateResponse(String response)
  {
    return new Boolean(response.equals(this.response));
  }

看到这里就很明显了,他是通过equals校验的。这个类是通过GimpyFactory来创建的,看看GimpyFactory的代码:

package com.octo.captcha.image.gimpy;


import com.octo.captcha.CaptchaException;
import com.octo.captcha.CaptchaQuestionHelper;
import com.octo.captcha.component.image.wordtoimage.WordToImage;
import com.octo.captcha.component.word.wordgenerator.WordGenerator;
import com.octo.captcha.image.ImageCaptcha;
import com.octo.captcha.image.ImageCaptchaFactory;
import java.awt.image.BufferedImage;
import java.security.SecureRandom;
import java.util.Locale;
import java.util.Random;


public class GimpyFactory
  extends ImageCaptchaFactory
{
  private Random myRandom = new SecureRandom();
  private WordToImage wordToImage;
  private WordGenerator wordGenerator;
  public static final String BUNDLE_QUESTION_KEY = Gimpy.class.getName();
  
  public GimpyFactory(WordGenerator generator, WordToImage word2image)
  {
    if (word2image == null) {
      throw new CaptchaException("Invalid configuration for a GimpyFactory : WordToImage can't be null");
    }
    if (generator == null) {
      throw new CaptchaException("Invalid configuration for a GimpyFactory : WordGenerator can't be null");
    }
    this.wordToImage = word2image;
    this.wordGenerator = generator;
  }
  
  public ImageCaptcha getImageCaptcha()
  {
    return getImageCaptcha(Locale.getDefault());
  }
  
  public WordToImage getWordToImage()
  {
    return this.wordToImage;
  }
  
  public WordGenerator getWordGenerator()
  {
    return this.wordGenerator;
  }
  
  public ImageCaptcha getImageCaptcha(Locale locale)
  {
    Integer wordLength = getRandomLength();
    
    String word = getWordGenerator().getWord(wordLength, locale);
    
    BufferedImage image = null;
    try
    {
      image = getWordToImage().getImage(word);
    }
    catch (Throwable e)
    {
      throw new CaptchaException(e);
    }
    ImageCaptcha captcha = new Gimpy(CaptchaQuestionHelper.getQuestion(locale, BUNDLE_QUESTION_KEY), image, word);//在此创建的Gimpy
    
    return captcha;
  }
  
  protected Integer getRandomLength()
  {
    int range = getWordToImage().getMaxAcceptedWordLength() - getWordToImage().getMinAcceptedWordLength();
    
    int randomRange = range != 0 ? this.myRandom.nextInt(range + 1) : 0;
    Integer wordLength = new Integer(randomRange + getWordToImage().getMinAcceptedWordLength());
    
    return wordLength;
  }
}


最后,重写下GimpyFactory和Gimpy。

GimpyFactory重写代码:

public class GimpyFactoryOverwrite extends ImageCaptchaFactory
{
 private Random myRandom = new SecureRandom();
 private WordToImage wordToImage;
 private WordGenerator wordGenerator;
 public static final String BUNDLE_QUESTION_KEY = Gimpy.class.getName();
 
 public GimpyFactoryOverwrite(WordGenerator generator, WordToImage word2image)
 {
   if (word2image == null) {
     throw new CaptchaException("Invalid configuration for a GimpyFactory : WordToImage can't be null");
   }
   if (generator == null) {
     throw new CaptchaException("Invalid configuration for a GimpyFactory : WordGenerator can't be null");
   }
   this.wordToImage = word2image;
   this.wordGenerator = generator;
 }
 
 public ImageCaptcha getImageCaptcha()
 {
   return getImageCaptcha(Locale.getDefault());
 }
 
 public WordToImage getWordToImage()
 {
   return this.wordToImage;
 }
 
 public WordGenerator getWordGenerator()
 {
   return this.wordGenerator;
 }
 
 public ImageCaptcha getImageCaptcha(Locale locale)
 {
   Integer wordLength = getRandomLength();
   
   String word = getWordGenerator().getWord(wordLength, locale);
   
   BufferedImage image = null;
   try
   {
     image = getWordToImage().getImage(word);
   }
   catch (Throwable e)
   {
     throw new CaptchaException(e);
   }
   ImageCaptcha captcha = new GimpyOverWrite(CaptchaQuestionHelper.getQuestion(locale, BUNDLE_QUESTION_KEY), image, word);
   
   return captcha;
 }
 
 protected Integer getRandomLength()
 {
   int range = getWordToImage().getMaxAcceptedWordLength() - getWordToImage().getMinAcceptedWordLength();
   
   int randomRange = range != 0 ? this.myRandom.nextInt(range + 1) : 0;
   Integer wordLength = new Integer(randomRange + getWordToImage().getMinAcceptedWordLength());
   
   return wordLength;
 }
}

Gimpy重写代码:

public class GimpyOverWrite extends ImageCaptcha implements Serializable {
/**

*/
private static final long serialVersionUID = 4721070331461038498L;

private String response;


GimpyOverWrite(String question, BufferedImage challenge, String response)
{
super(question, challenge);
this.response = response;
}


public final Boolean validateResponse(Object response)
{
return (null != response) && ((response instanceof String)) ? validateResponse((String)response) : Boolean.FALSE;
}


private final Boolean validateResponse(String response)
{
return new Boolean(response.equalsIgnoreCase(this.response));
}
}


最后别忘了在验证码工厂替换下类:

<!-- 验证码工厂(此类为重写captcha内GimpyFactory类,为了实现验证码不区分大小写) -->
<bean id="captchaFactory" class="com.cpic.jvglapp.claimserver.entity.captcha.GimpyFactoryOverwrite">
<constructor-arg>
<ref bean="wordgen" />
</constructor-arg>
<constructor-arg>
<ref bean="wordtoimage" />
</constructor-arg>
</bean>


写完收工再见

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值