轉自:
http://my.oschina.net/jawava/blog/8574
發(fā)現(xiàn)這個社區(qū)不錯,所以也湊個熱鬧。
第一篇日志,一定要動手寫才有誠意。
這兩天要給剛做的外網(wǎng)系統(tǒng)登錄頁面加驗證碼,以前沒做過。上網(wǎng)搜了一下,資料很多。
驗證碼校驗稱作captcha:
Completely Automated Public Test to tell Computers and Humans Apart
專業(yè)點兒的翻譯是:全自動區(qū)分計算機和人類的圖靈測試。
CAPTCHA的目的很明確,就是區(qū)分計算機和人類的一種程序算法,
這種程序必須能生成并評價人類能很容易通過但計算機卻通不過的測試。
網(wǎng)上能查到不少實現(xiàn)方案,簡單的寫個jsp就行了,技術含量不高。搜了一圈后,
感覺還是用個正規(guī)點兒比較合適,然后就鎖定了JCaptcha,到其官方網(wǎng)站上看了看:
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)
它用上百個類來實現(xiàn)了如此簡單的功能,這是為什么呢?官方給的解釋是
1、學術界能不斷的發(fā)明(或發(fā)現(xiàn))一些人類容易處理而機器不能很好處理的問題。
JCaptcha高屋建瓴的給出了一種通用的定義和表達這種問題并用于識別的方案。
也就是識別方案的可擴展性。
2、實現(xiàn)了若干引擎和組件,通過配置這些引擎和組件,可以方便的修改自己程序
captcha構件的算法。這樣,在抵御惡意訪問時,可以不用改變代碼,靈活快速的
改變captcha策略,從而更好的保護系統(tǒng)。
個人覺的說的挺好,第一點對于我們來說倒是次要的,主要第二點比較有意義。
然后就試了試。下面是具體需要做的工作:
一,從官網(wǎng)上下個jcaptcha-1.0-all.jar,加入到項目中,
官方的2.0還沒有正式版,所以先用1.0吧。
二,官網(wǎng)上介紹了幾種和項目結合的具體方案,最簡單的方式很快走通,圖片很難看,
而且不具有可配置性,肯定不行。所以選擇通過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><!--最大并發(fā)數(shù)--> <constructor-arg index="3"><value>20000</value></constructor-arg>& lt;!--第四個參數(shù)官網(wǎng)示例上沒有給出,會報錯,后來看了API才知道少了個參數(shù)--> </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>文字產(chǎn)生器,提供了好幾種實現(xiàn),經(jīng)過比較選用了這種</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><!--字符最少個數(shù)--> </constructor-arg> <constructor-arg type="java.lang.Integer" index="1"> <value>4</value><!--字符最多個數(shù)--> </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,我把包里提供的現(xiàn)成的組件基本上試了大半,
最后選擇了如上的配置,選擇的標準一是美觀,二是識別率。識別率太低了,用戶體驗會下降。
三、配置好后,JCaptcha這邊的工作就完成了。下面就是和項目的結合。
1、首先專門寫個CaptcahServlet用來獲取驗證碼,由于要在servlet里注入spring的bean,
所以用了代理的方式。代理類網(wǎng)上有,都是固定寫法,這里就不貼了。
CaptcahServlet主要部分如下:
@Override public void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException, RuntimeException {
byte[] captchaChallengeAsJpeg = null; //輸出jpg的字節(jié)流 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、驗證環(huán)節(jié)。我是在原來的驗證用戶密碼的邏輯前,加上了驗證碼的校驗邏輯。
很簡單的幾句話,代碼如下:
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("校驗驗證碼時出現(xiàn)不明錯誤",e); } |
四、如此就可以測試了。通過反復調(diào)整參數(shù),達到了比較美觀的效果,但是文字始終
不在圖片的正中,而是靠上,甚至都把文字掩蓋了一半。翻看了半天API,也看不出
個端倪。只好去翻源代碼了。問題出在
com.octo.captcha.component.image.textpaster.SimpleTextPaster這個類,它把文字
圖片往背景圖片放時,把文字的位置放在了背景圖片一半的高度上,這就是問題所在。
修改代碼重新打了個jar包。替換一下,重啟后一切ok。
posted @
2012-10-18 10:16 achan2bj 閱讀(1308) |
評論 (2) |
編輯 收藏
Vector 還是ArrayList――哪一個更好,為什么?
要回答這個問題不能一概而論,有時候使用Vector比較好;有時是ArrayList,有時候這兩個都不是
最好的選擇。你別指望能夠獲得一個簡單肯定答案,因為這要看你用它們干什么。下面有4個要考慮
的因素:
l API
l 同步處理
l 數(shù)據(jù)增長性
l 使用模式
下面針對這4個方面進行一一探討
API
在由Ken Arnold等編著的《Java Programming Language》(Addison-Wesley, June 2000)一書中有這
樣的描述,Vector類似于ArrayList.。所有從API的角度來看這兩個類非常相[b]似。但他們之間也還
是有一些主要的區(qū)別的。
同步性
Vector是同步的。這個類中的一些方法保證了Vector中的對象是線程安全的。而ArrayList則是異步
的,因此ArrayList中的對象并不是線程安全的。因為同步的要求會影響執(zhí)行的效率,所以如果你不
需要線程安全的集合那么使用ArrayList是一個很好的選擇,這樣可以避免由于同步帶來的不必要的
性能開銷。
數(shù)據(jù)增長
從內(nèi)部實現(xiàn)機制來講ArrayList和Vector都是使用數(shù)組(Array)來控制集合中的對象。當你向這兩種類
型中增加元素的時候,如果元素的數(shù)目超出了內(nèi)部數(shù)組目前的長度它們都需要擴展內(nèi)部數(shù)組的長度,
Vector缺省情況下自動增長原來一倍的數(shù)組長度,ArrayList是原來的50%,所以最后你獲得的這個集
合所占的空間總是比你實際需要的要大。所以如果你要在集合中保存大量的數(shù)據(jù)那么使用Vector有一
些優(yōu)勢,因為你可以通過設置集合的初始化大小來避免不必要的資源開銷。
使用模式
在ArrayList和Vector中,從一個指定的位置(通過索引)查找數(shù)據(jù)或是在集合的末尾增加、移除一
個元素所花費的時間是一樣的,這個時間我們用O(1)表示。但是,如果在集合的其他位置增加或移除
元素那么花費的時間會呈線形增長:O(n-i),其中n代表集合中元素的個數(shù),i代表元素增加或移除元
素的索引位置。為什么會這樣呢?以為在進行上述操作的時候集合中第i和第i個元素之后的所有元素
都要執(zhí)行位移的操作。這一切意味著什么呢?
這意味著,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或
ArrayList都可以。如果是其他操作,你最好選擇其他的集合操作類。比如,LinkList集合類在增加
或移除集合中任何位置的元素所花費的時間都是一樣的—O(1),但它在索引一個元素的使用缺比較慢
-O(i),其中i是索引的位置.使用ArrayList也很容易,因為你可以簡單的使用索引來代替創(chuàng)建
iterator對象的操作。LinkList也會為每個插入的元素創(chuàng)建對象,所有你要明白它也會帶來額外的開
銷。
最后,在《Practical Java》一書中Peter Haggar建議使用一個簡單的數(shù)組(Array)來代替Vector
或ArrayList。尤其是對于執(zhí)行效率要求高的程序更應如此。因為使用數(shù)組(Array)避免了同步、額外
的方法調(diào)用和不必要的重新分配空間的操作。
posted @
2012-09-29 09:38 achan2bj|
編輯 收藏
摘要: JAVA相關基礎知識1、面向?qū)ο蟮奶卣饔心男┓矫?1.抽象:抽象就是忽略一個主題中與當前目標無關的那些方面,以便更充分地注意與當前目標有關的方面。抽象并不打算了解全部問題,而只是選擇其中的一部分,暫時不用部分細節(jié)。抽象包括兩個方面,一是過程抽象,二是數(shù)據(jù)抽象。2.繼承:繼承是一種聯(lián)結類的層次模型,并且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。對象的一個新類可以從現(xiàn)有的類中派生,這個過程稱...
閱讀全文
posted @
2012-09-29 09:31 achan2bj|
編輯 收藏