使用Jcaptcha做验证码总结http://my.oschina.net/jawava/blog/8574?发现这个社区不错,所以也凑个热闹。第一
使用Jcaptcha做验证码总结
http://my.oschina.net/jawava/blog/8574
?
发现这个社区不错,所以也凑个热闹。
第一篇日志,一定要动手写才有诚意。
这两天要给刚做的外网系统登录页面加验证码,以前没做过。上网搜了一下,资料很多。
验证码校验称作captcha:
Completely Automated Public Test to tell Computers and Humans Apart
专业点儿的翻译是:全自动区分计算机和人类的图灵测试。
CAPTCHA的目的很明确,就是区分计算机和人类的一种程序算法,
这种程序必须能生成并评价人类能很容易通过但计算机却通不过的测试。
网上能查到不少实现方案,简单的写个jsp就行了,技术含量不高。搜了一圈后,
感觉还是用个正规点儿比较合适,然后就锁定了JCaptcha,到其官方网站上看了看:
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
????????"http://www.springframework.org/dtd/spring-beans.dtd">
<beans??default-autowire="byName">
????<bean id="captchaServlet" />
<bean id="captchaService" >
<description>验证码工厂</description>
<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" >
<description>文字产生器,提供了好几种实现,经过比较选用了这种</description>
<constructor-arg index="0"><value>0123456789</value></constructor-arg>
</bean>
<bean id="wordtoimage" >
<description>图片生成器</description>
<constructor-arg index="0"><ref bean="fontGenRandom"/></constructor-arg>
<constructor-arg index="1"><ref bean="backGenUni"/></constructor-arg>
<constructor-arg index="2"><ref bean="simpleWhitePaster"/></constructor-arg>
</bean>
<bean id="fontGenRandom" >
<description>文字转换图片</description>
<constructor-arg index="0"><value>20</value></constructor-arg><!--字体最小尺寸-->
<constructor-arg index="1"><value>20</value></constructor-arg><!--字体最大尺寸-->
</bean>?
<bean id="backGenUni" >
<constructor-arg index="0"><value>62</value></constructor-arg><!--背景图片宽度-->
<constructor-arg index="1"><value>22</value></constructor-arg><!--背景图片高度-->
<constructor-arg type="java.awt.Color" index="2">
<ref bean="colorGrey"/>
</constructor-arg>?
<constructor-arg type="java.awt.Color" index="3">
<ref bean="colorGreen"/>
</constructor-arg>
</bean>
<bean id="simpleWhitePaster" >
<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 type="java.awt.Color" index="2">
<ref bean="colorFont"/>
</constructor-arg>
</bean>
<bean id="colorGrey" >
<constructor-arg index="0"><value>200</value></constructor-arg>
<constructor-arg index="1"><value>255</value></constructor-arg>
<constructor-arg index="2"><value>200</value></constructor-arg>
</bean>
<bean id="colorGreen" >
<constructor-arg index="0"><value>110</value></constructor-arg>
<constructor-arg index="1"><value>120</value></constructor-arg>
<constructor-arg index="2"><value>200</value></constructor-arg>
</bean>
<bean id="colorFont" >
<constructor-arg index="0"><value>60</value></constructor-arg>
<constructor-arg index="1"><value>60</value></constructor-arg>
<constructor-arg index="2"><value>60</value></constructor-arg>
</bean>
</beans>
这里面具体用哪个component,需要看ApI,我把包里提供的现成的组件基本上试了大半,
最后选择了如上的配置,选择的标准一是美观,二是识别率。识别率太低了,用户体验会下降。
三、配置好后,JCaptcha这边的工作就完成了。下面就是和项目的结合。
1、首先专门写个CaptcahServlet用来获取验证码,由于要在servlet里注入spring的bean,
所以用了代理的方式。代理类网上有,都是固定写法,这里就不贴了。
CaptcahServlet主要部分如下:
@Override
public void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws ServletException, IOException, RuntimeException {
byte[] captchaChallengeAsJpeg = null;
//输出jpg的字节流
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
// get the session id that will identify the generated captcha.
//the same id must be used to validate the response, the session id is a good candidate!
String captchaId = httpServletRequest.getSession().getId();
// call the ImageCaptchaService getChallenge method
BufferedImage challenge =
(BufferedImage) captchaService.getChallengeForID(captchaId,
????????????httpServletRequest.getLocale());
// a jpeg encoder
????JPEGImageEncoder jpegEncoder =
????????????JPEGCodec.createJPEGEncoder(jpegOutputStream);
????jpegEncoder.encode(challenge);
} catch (IllegalArgumentException e) {
????httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
????return;
} catch (CaptchaServiceException e) {
????httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
????return;
}
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
// flush it in the response
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/jpeg");
ServletOutputStream responseOutputStream =
????????httpServletResponse.getOutputStream();
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
responseOutputStream.close();
????}
然后在web.xml配置:
<servlet>
<servlet-name>CaptchaProxy</servlet-name>
<servlet-class>com.jawava.XXXXX.XXX.TopHttpServletProxy</servlet-class>
<init-param>
<param-name>targetServlet</param-name>
<param-value>captchaServlet</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>CaptchaProxy</servlet-name>
<url-pattern>/topJcaptcha</url-pattern>
</servlet-mapping>
2、目前项目的登录页面是jsp,结合很方便,在原有页面上加上
<img src="/topJcaptcha" /> <input type="text" name="jcaptcha" value="" />
那个img就是校验码。
需要的话,再加上个换图片的功能,很简单。
3、验证环节。我是在原来的验证用户密码的逻辑前,加上了验证码的校验逻辑。
很简单的几句话,代码如下:
Boolean isResponseCorrect =Boolean.FALSE;
????????//需要sessionId 来验证校验码
????????String sessionId = request.getSession().getId();
//首先校验验证码
????????try {
????????????isResponseCorrect = captchaService.validateResponseForID(sessionId,
???????????? captcha_input);
????????????if(!isResponseCorrect) {
???????????? throw new RuntimeException("输入的验证码有误,请重新输入");
????????????}
????????} catch (CaptchaServiceException e) {
???????????? //should not happen, may be thrown if the id is not valid
???????? throw new RuntimeException("校验验证码时出现不明错误",e);
????????}
四、如此就可以测试了。通过反复调整参数,达到了比较美观的效果,但是文字始终
不在图片的正中,而是靠上,甚至都把文字掩盖了一半。翻看了半天API,也看不出
个端倪。只好去翻源代码了。问题出在
com.octo.captcha.component.image.textpaster.SimpleTextPaster这个类,它把文字
图片往背景图片放时,把文字的位置放在了背景图片一半的高度上,这就是问题所在。
修改代码重新打了个jar包。替换一下,重启后一切ok。