转自:
http://my.oschina.net/jawava/blog/8574
发现这个社区不错,所以也凑个热闹。
第一篇日志,一定要动手写才有诚意。
这两天要给刚做的外网系统登录页面加验证码,以前没做过。上网搜了一下,资料很多。
验证码校验称作captcha:
Completely Automated Public Test to tell Computers and Humans Apart
专业点儿的翻译是:全自动区分计算机和人类的图灵测试。
CAPTCHA的目的很明确,就是区分计算机和人类的一种程序算法,
这种程序必须能生成并评价人类能很容易通过但计算机却通不过的测试。
网上能查到不少实现方案,简单的写个jsp就行了,技术含量不高。搜了一圈后,
感觉还是用个正规点儿比较合适,然后就锁定了JCaptcha,到其官方网站上看了看:
http://jcaptcha.octo.com/confluence/display/general/HomeJCaptcha提供了
Provide robust and reliable CAPTCHA implementation framework for JAVA
Provide accessible CAPTCHA implementations
Provide multi-type challenge (text, sound, image)
它用上百个类来实现了如此简单的功能,这是为什么呢?官方给的解释是
1、学术界能不断的发明(或发现)一些人类容易处理而机器不能很好处理的问题。
JCaptcha高屋建瓴的给出了一种通用的定义和表达这种问题并用于识别的方案。
也就是识别方案的可扩展性。
2、实现了若干引擎和组件,通过配置这些引擎和组件,可以方便的修改自己程序
captcha构件的算法。这样,在抵御恶意访问时,可以不用改变代码,灵活快速的
改变captcha策略,从而更好的保护系统。
个人觉的说的挺好,第一点对于我们来说倒是次要的,主要第二点比较有意义。
然后就试了试。下面是具体需要做的工作:
一,从官网上下个jcaptcha-1.0-all.jar,加入到项目中,
官方的2.0还没有正式版,所以先用1.0吧。
二,官网上介绍了几种和项目结合的具体方案,最简单的方式很快走通,图片很难看,
而且不具有可配置性,肯定不行。所以选择通过spring来整合的方式,
spring是整合和配置的平台,把jcaptcha的服务和引擎还有组件配置成spring的bean。
示例如下:
<?xml version="1.0" encoding="gb2312"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans default-autowire="byName">
<bean id="captchaServlet" class="com.jawava.XXXX.XXX.TopImageCaptchaServlet" />
<bean id="captchaService" class="com.octo.captcha.service.multitype.GenericManageableCaptchaService"> <description>验证码服务</description> <constructor-arg index="0"><ref bean="imageEngine"/></constructor-arg> <constructor-arg index="1"><value>300</value></constructor-arg><!--超时时间 秒--> <constructor-arg index="2"><value>20000</value></constructor-arg><!--最大并发数--> <constructor-arg index="3"><value>20000</value></constructor-arg>& lt;!--第四个参数官网示例上没有给出,会报错,后来看了API才知道少了个参数--> </bean>
<bean id="imageEngine" class="com.octo.captcha.engine.GenericCaptchaEngine"> <description>图片引擎</description> <constructor-arg index="0"> <list> <ref bean="CaptchaFactory"/> </list> </constructor-arg> </bean>
<bean id="CaptchaFactory" class="com.octo.captcha.image.gimpy.GimpyFactory" > <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" class="com.octo.captcha.component.image.wordtoimage.ComposedWordToImage" > <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" class="com.octo.captcha.component.image.fontgenerator.RandomFontGenerator" > <description>文字转换图片</description> <constructor-arg index="0"><value>20</value></constructor-arg><!--字体最小尺寸--> <constructor-arg index="1"><value>20</value></constructor-arg><!--字体最大尺寸--> </bean>
<bean id="backGenUni" class="com.octo.captcha.component.image.backgroundgenerator.GradientBackgroundGenerator" > <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" class="com.octo.captcha.component.image.textpaster.SimpleTextPaster" > <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" class="java.awt.Color" > <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" class="java.awt.Color" > <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" class="java.awt.Color" > <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。
posted @
2012-10-18 10:16 achan2bj 阅读(1302) |
评论 (2) |
编辑 收藏
Vector 还是ArrayList――哪一个更好,为什么?
要回答这个问题不能一概而论,有时候使用Vector比较好;有时是ArrayList,有时候这两个都不是
最好的选择。你别指望能够获得一个简单肯定答案,因为这要看你用它们干什么。下面有4个要考虑
的因素:
l API
l 同步处理
l 数据增长性
l 使用模式
下面针对这4个方面进行一一探讨
API
在由Ken Arnold等编著的《Java Programming Language》(Addison-Wesley, June 2000)一书中有这
样的描述,Vector类似于ArrayList.。所有从API的角度来看这两个类非常相[b]似。但他们之间也还
是有一些主要的区别的。
同步性
Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步
的,因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率,所以如果你不
需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于同步带来的不必要的
性能开销。
数据增长
从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类
型中增加元素的时候,如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度,
Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%,所以最后你获得的这个集
合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一
些优势,因为你可以通过设置集合的初始化大小来避免不必要的资源开销。
使用模式
在ArrayList和Vector中,从一个指定的位置(通过索引)查找数据或是在集合的末尾增加、移除一
个元素所花费的时间是一样的,这个时间我们用O(1)表示。但是,如果在集合的其他位置增加或移除
元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加或移除元
素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素
都要执行位移的操作。这一切意味着什么呢?
这意味着,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或
ArrayList都可以。如果是其他操作,你最好选择其他的集合操作类。比如,LinkList集合类在增加
或移除集合中任何位置的元素所花费的时间都是一样的—O(1),但它在索引一个元素的使用缺比较慢
-O(i),其中i是索引的位置.使用ArrayList也很容易,因为你可以简单的使用索引来代替创建
iterator对象的操作。LinkList也会为每个插入的元素创建对象,所有你要明白它也会带来额外的开
销。
最后,在《Practical Java》一书中Peter Haggar建议使用一个简单的数组(Array)来代替Vector
或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外
的方法调用和不必要的重新分配空间的操作。
posted @
2012-09-29 09:38 achan2bj|
编辑 收藏
摘要: JAVA相关基础知识1、面向对象的特征有哪些方面 1.抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。2.继承:继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称...
阅读全文
posted @
2012-09-29 09:31 achan2bj|
编辑 收藏