posts - 6,comments - 2,trackbacks - 0
          轉(zhuǎn)自:
          http://my.oschina.net/jawava/blog/8574


          發(fā)現(xiàn)這個(gè)社區(qū)不錯(cuò),所以也湊個(gè)熱鬧。

          第一篇日志,一定要?jiǎng)邮謱懖庞姓\意。

          這兩天要給剛做的外網(wǎng)系統(tǒng)登錄頁面加驗(yàn)證碼,以前沒做過。上網(wǎng)搜了一下,資料很多。
          驗(yàn)證碼校驗(yàn)稱作captcha:
          Completely Automated Public Test to tell Computers and Humans Apart
          專業(yè)點(diǎn)兒的翻譯是:全自動(dòng)區(qū)分計(jì)算機(jī)和人類的圖靈測試。
          CAPTCHA的目的很明確,就是區(qū)分計(jì)算機(jī)和人類的一種程序算法,
          這種程序必須能生成并評價(jià)人類能很容易通過但計(jì)算機(jī)卻通不過的測試。

          網(wǎng)上能查到不少實(shí)現(xiàn)方案,簡單的寫個(gè)jsp就行了,技術(shù)含量不高。搜了一圈后,
          感覺還是用個(gè)正規(guī)點(diǎn)兒比較合適,然后就鎖定了JCaptcha,到其官方網(wǎng)站上看了看:
          http://jcaptcha.octo.com/confluence/display/general/Home

          JCaptcha提供了
          Provide robust and reliable CAPTCHA implementation framework for JAVA
          Provide accessible CAPTCHA implementations
          Provide multi-type challenge (text, sound, image)

          它用上百個(gè)類來實(shí)現(xiàn)了如此簡單的功能,這是為什么呢?官方給的解釋是
          1、學(xué)術(shù)界能不斷的發(fā)明(或發(fā)現(xiàn))一些人類容易處理而機(jī)器不能很好處理的問題。
          JCaptcha高屋建瓴的給出了一種通用的定義和表達(dá)這種問題并用于識(shí)別的方案。
          也就是識(shí)別方案的可擴(kuò)展性。
          2、實(shí)現(xiàn)了若干引擎和組件,通過配置這些引擎和組件,可以方便的修改自己程序
          captcha構(gòu)件的算法。這樣,在抵御惡意訪問時(shí),可以不用改變代碼,靈活快速的
          改變captcha策略,從而更好的保護(hù)系統(tǒng)。

          個(gè)人覺的說的挺好,第一點(diǎn)對于我們來說倒是次要的,主要第二點(diǎn)比較有意義。
          然后就試了試。下面是具體需要做的工作:

          一,從官網(wǎng)上下個(gè)jcaptcha-1.0-all.jar,加入到項(xiàng)目中,
          官方的2.0還沒有正式版,所以先用1.0吧。

          二,官網(wǎng)上介紹了幾種和項(xiàng)目結(jié)合的具體方案,最簡單的方式很快走通,圖片很難看,
          而且不具有可配置性,肯定不行。所以選擇通過spring來整合的方式,
          spring是整合和配置的平臺(tái),把jcaptcha的服務(wù)和引擎還有組件配置成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>驗(yàn)證碼服務(wù)</description>
          <constructor-arg index="0"><ref bean="imageEngine"/></constructor-arg>
          <constructor-arg index="1"><value>300</value></constructor-arg><!--超時(shí)時(shí)間 秒-->
          <constructor-arg index="2"><value>20000</value></constructor-arg><!--最大并發(fā)數(shù)-->
          <constructor-arg index="3"><value>20000</value></constructor-arg>& lt;!--第四個(gè)參數(shù)官網(wǎng)示例上沒有給出,會(huì)報(bào)錯(cuò),后來看了API才知道少了個(gè)參數(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>驗(yàn)證碼工廠</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)生器,提供了好幾種實(shí)現(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>文字轉(zhuǎn)換圖片</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><!--字符最少個(gè)數(shù)-->
          </constructor-arg>
          <constructor-arg type="java.lang.Integer" index="1">
          <value>4</value><!--字符最多個(gè)數(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>



          這里面具體用哪個(gè)component,需要看ApI,我把包里提供的現(xiàn)成的組件基本上試了大半,
          最后選擇了如上的配置,選擇的標(biāo)準(zhǔn)一是美觀,二是識(shí)別率。識(shí)別率太低了,用戶體驗(yàn)會(huì)下降。

          三、配置好后,JCaptcha這邊的工作就完成了。下面就是和項(xiàng)目的結(jié)合。
          1、首先專門寫個(gè)CaptcahServlet用來獲取驗(yàn)證碼,由于要在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、目前項(xiàng)目的登錄頁面是jsp,結(jié)合很方便,在原有頁面上加上
          <img src="/topJcaptcha" /> <input type="text" name="jcaptcha" value="" />
          那個(gè)img就是校驗(yàn)碼。
          需要的話,再加上個(gè)換圖片的功能,很簡單。

          3、驗(yàn)證環(huán)節(jié)。我是在原來的驗(yàn)證用戶密碼的邏輯前,加上了驗(yàn)證碼的校驗(yàn)邏輯。
          很簡單的幾句話,代碼如下:

          Boolean isResponseCorrect =Boolean.FALSE;
                  //需要sessionId 來驗(yàn)證校驗(yàn)碼
                  String sessionId = request.getSession().getId();
          //首先校驗(yàn)驗(yàn)證碼
                  try {
                      isResponseCorrect = captchaService.validateResponseForID(sessionId,
                       captcha_input);
                      if(!isResponseCorrect) {
                       throw new RuntimeException("輸入的驗(yàn)證碼有誤,請重新輸入");
                      }
                  } catch (CaptchaServiceException e) {
                       //should not happen, may be thrown if the id is not valid
                   throw new RuntimeException("校驗(yàn)驗(yàn)證碼時(shí)出現(xiàn)不明錯(cuò)誤",e);
                  }



          四、如此就可以測試了。通過反復(fù)調(diào)整參數(shù),達(dá)到了比較美觀的效果,但是文字始終
          不在圖片的正中,而是靠上,甚至都把文字掩蓋了一半。翻看了半天API,也看不出
          個(gè)端倪。只好去翻源代碼了。問題出在
          com.octo.captcha.component.image.textpaster.SimpleTextPaster這個(gè)類,它把文字
          圖片往背景圖片放時(shí),把文字的位置放在了背景圖片一半的高度上,這就是問題所在。
          修改代碼重新打了個(gè)jar包。替換一下,重啟后一切ok。
          posted @ 2012-10-18 10:16 achan2bj 閱讀(1314) | 評論 (2)編輯 收藏
          主站蜘蛛池模板: 高安市| 泰顺县| 拜城县| 普兰店市| 自治县| 潜江市| 西乌珠穆沁旗| 镇雄县| 观塘区| 临沂市| 定南县| 鞍山市| 集贤县| 繁峙县| 和平区| 探索| 石嘴山市| 长阳| 江川县| 上蔡县| 阿尔山市| 紫阳县| 康定县| 日照市| 通辽市| 新乡县| 北票市| 南岸区| 隆林| 文昌市| 成安县| 郑州市| 泸定县| 北辰区| 金湖县| 潜山县| 九江市| 南溪县| 福泉市| 高州市| 西畴县|