1. 程式人生 > >Kaptcha實現的驗證碼功能

Kaptcha實現的驗證碼功能

驗證碼分析

1.    選型

jQuery.buttonCaptcha需要引用jquery一系列檔案,對於沒有采用jquery的系統中會造成載入資源過多,影響頁面速度。

Kaptcha使用java來生成驗證碼,可配置(自定義)驗證碼的型別(中文,字母,數字)、字型型別、字型顏色、圖片的寬度和高度、圖片邊框、干擾線、樣式等。可配置,因此使用方便;而且支援擴充套件。

基於以上判斷,我建議使用kaptcha。

2.    Kaptcha的使用

Kaptcha使用很方便,通過servlet來訪問。在web專案中,可以這樣配置。

<servlet>

<servlet-name>kaptcha</servlet-name>

<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>

<init-param>

<param-name>kaptcha.border</param-name>

<param-value>no</param-value>

</init-param>

<init-param>

<param-name>kaptcha.border.color</param-name>

<param-value>105,179,90</param-value>

</init-param>

<init-param>

<param-name>kaptcha.textproducer.font.color</param-name>

<param-value>red</param-value>

</init-param>

<init-param>

<param-name>kaptcha.image.width</param-name>

<param-value>200</param-value>

</init-param>

<init-param

<param-name>kaptcha.image.height</param-name>

<param-value>50</param-value>

</init-param>

<init-param>

<param-name>kaptcha.textproducer.font.size</param-name>

<param-value>40</param-value>

</init-param>

<init-param>

<param-name>kaptcha.textproducer.char.length</param-name>

<param-value>4</param-value>

</init-param>

<init-param>

<param-name>kaptcha.textproducer.font.names</param-name>

<param-value>仿宋_GB2312</param-value>

</init-param>

</servlet>

<servlet-mapping>

<servlet-name>kaptcha</servlet-name>

<url-pattern>/kaptcha.jpg</url-pattern>

</servlet-mapping>

在頁面中,可以這樣使用

<img src="../kaptcha.jpg"id="aptcha">

Kaptcha在生成驗證碼圖片的同時,會將驗證碼的內容放入session中,key是com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY。這樣,我們就可以在服務端驗證使用者輸入的驗證碼是否正確了。

以上配置是針對普通web專案的,如果採用了spring mvc框架的專案,要採取另外一種配置方式,這種配置方式session中無法獲得驗證碼的值。

在spring mvc中,要為它單獨配置一個controller

首先新建一個檔案 applicationContext-kaptcha.xml

<?xmlversion="1.0" encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<beanid="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">

<propertyname="config">

<beanclass="com.google.code.kaptcha.util.Config">

<constructor-arg>

<props>

<propkey="kaptcha.border">no</prop>

<propkey="kaptcha.border.color">105,179,90</prop>

<propkey="kaptcha.textproducer.font.color">blue</prop>

<propkey="kaptcha.image.width">130</prop>

<propkey="kaptcha.textproducer.font.size">30</prop>

<propkey="kaptcha.image.height">40</prop>

<propkey="kaptcha.textproducer.char.length">4</prop>

<propkey="kaptcha.textproducer.font.names">宋體,楷體,微軟雅黑</prop>

</props>

</constructor-arg>

</bean>

</property>

</bean>

</beans>

之後,新建一個controller

package cn.com.gei.htic.platform.portal.controller;

importjava.awt.image.BufferedImage;

import javax.imageio.ImageIO;

importjavax.servlet.ServletOutputStream;

importjavax.servlet.http.HttpServletRequest;

importjavax.servlet.http.HttpServletResponse;

importorg.springframework.beans.factory.annotation.Autowired;

importorg.springframework.stereotype.Controller;

importorg.springframework.web.bind.annotation.RequestMapping;

importorg.springframework.web.servlet.ModelAndView;

importcom.google.code.kaptcha.Constants;

importcom.google.code.kaptcha.Producer;

@Controller

@RequestMapping("/kaptcha")

@Controller

public classKaptchaController {

privateProducer captchaProducer = null;

@Autowired

publicvoid setCaptchaProducer(Producer captchaProducer) {

this.captchaProducer= captchaProducer;

}

@RequestMapping("/captcha-image")

publicModelAndView handleRequest(HttpServletRequest request,

            HttpServletResponse response) throws Exception {

        response.setDateHeader("Expires", 0);

        // Set standard HTTP/1.1 no-cache headers.

        response.setHeader("Cache-Control",

                "no-store, no-cache, must-revalidate");

        // Set IE extended HTTP/1.1 no-cache headers (use addHeader).

        response.addHeader("Cache-Control","post-check=0, pre-check=0");

        // Set standard HTTP/1.0 no-cache header.

        response.setHeader("Pragma", "no-cache");

        // return a jpeg

        response.setContentType("image/jpeg");

        // create the text for the image

        String capText = captchaProducer.createText();

        // store the text in the session

        request.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY,

                capText);

        // create the image with the text

        BufferedImage bi = captchaProducer.createImage(capText);

        ServletOutputStream out = response.getOutputStream();

        // write the data out

        ImageIO.write(bi, "jpg", out);

        try {

            out.flush();

        } finally {

            out.close();

        }

        return null;

    }

}

然後新建一個aptcha-servlet.xml,放在WEB-INF下

<?xmlversion="1.0" encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:context="http://www.springframework.org/schema/context"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

    http://www.springframework.org/schema/context

    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan

        base-package=" cn.com.gei.htic.platform.portal.controller"/>

    <bean

        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

    <bean id="viewResolver"

        class="org.springframework.web.servlet.view.UrlBasedViewResolver">

        <property name="viewClass"

            value="org.springframework.web.servlet.view.JstlView"/>

        <property name="prefix"value="/WEB-INF/jsp/attachment/" />

        <property name="suffix" value=".jsp"/>

    </bean>

</beans>

最後在web.xml中配置

<servlet>

        <servlet-name>aptcha</servlet-name>

        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

        <load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

        <servlet-name>aptcha</servlet-name>

        <url-pattern>/aptcha/*</url-pattern>

</servlet-mapping>

在頁面中,可以通過如下方式來使用:

<img src="/kaptcha/captcha-image"></img>

3.    與Spring Security結合

這裡 的與Spring Security結合,是指登入頁面的驗證碼驗證問題。

首先在Spring Security的配置檔案中增加如下內容:

<intercept-url pattern="/aptcha/captcha-image"filters="none" />

意思是不對驗證碼進行許可權驗證

之後要自定義一個filter

在<http></http>中增加

<custom-filterref="aptchaFilter" position="PRE_AUTH_FILTER" />

PRE_AUTH_FILTER的意思是在Spring Security驗證之前進行的驗證,具體可以參考這裡

之後增加aptchaFilter的定義

<b:beanid="aptchaFilter"

        class="cn.com.gei.kmp4.kjjexternal.security.filter.AptchaFilter">

    <b:property name="authenticationManager"ref="authenticationManager"></b:property>

    <b:property name="authenticationSuccessHandler"ref="loginSuccessHandler"></b:property>

    <b:property name="authenticationFailureHandler"ref="simpleUrlAuthenticationFailureHandler"></b:property>

</b:bean>

<b:beanid="simpleUrlAuthenticationFailureHandler"

        class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">

    <b:property name="defaultFailureUrl"value="/home/index"></b:property>

</b:bean>

引用的authenticationManager是別名

<authentication-manageralias="authenticationManager">

<authentication-providerref="daoAuthenticationProvider">

</authentication-provider>

</authentication-manager>

其中loginSuccessHandler可以採用kmp4預設的。

AptchaFilter的定義如下:

public class AptchaFilterextends UsernamePasswordAuthenticationFilter {

    @Override

    public Authentication attemptAuthentication(HttpServletRequestrequest,

            HttpServletResponse response) throws AuthenticationException{

        checkValidateCode(request);

        return super.attemptAuthentication(request, response);

    }

    protected void checkValidateCode(HttpServletRequest request) {

        String sessionValidateCode =obtainSessionValidateCode(request);

        String validateCodeParameter =obtainValidateCodeParameter(request);

        if (sessionValidateCode != null

                &&!sessionValidateCode.equalsIgnoreCase(validateCodeParameter)) {

            throw new AuthenticationServiceException(messages

                    .getMessage("validateCode.notEquals"));

        }

    }

    private String obtainSessionValidateCode(HttpServletRequestrequest) {

        HttpSession session = request.getSession(true);

        String code = (String) session

                .getAttribute(Constants.KAPTCHA_SESSION_KEY);

        return code;

    }

    private String obtainValidateCodeParameter(HttpServletRequestrequest) {

      //yanzheng 是頁面中驗證碼輸入框的name

        return request.getParameter("yanzheng");

    }

}

此filter繼承自 UsernamePasswordAuthenticationFilter,重寫了attemptAuthentication方法,在此方法中,我們可以增加對驗證碼的驗證邏輯

如果驗證失敗,則丟擲AuthenticationServiceException異常,異常的提示資訊可以結合本地message策略,在org/springframework/security/messages_zh_CN.properties中增加一條,這裡採用的是

validateCode.notEquals    驗證碼不正確

通過配置simpleUrlAuthenticationFailureHandler,我們就可以在這個filter驗證失敗後,將頁面重定向到登入頁面,從而實現驗證碼的驗證了。

4.    引數含義

Constant

Description

Default

常數

描述

預設值

kaptcha.border

有無邊框,選項是yes或者no

Yes

kaptcha.border.color

邊框顏色。可選值為red,green,blue 或者white,black,blue

Black

kaptcha.border.thickness

邊框厚度,可選值為大於0的整數

1

kaptcha.image.width

圖片寬度(畫素)

200

kaptcha.image.height

圖片高度(畫素)

50

kaptcha.producer.impl

圖片生產者

com.google.code.kaptcha.impl.DefaultKaptcha  (可自定義,實現Producer介面即可)

kaptcha.textproducer.impl

字型產生者

com.google.code.kaptcha.text.impl.DefaultTextCreator(可自定義,實現TextProducer介面即可)

kaptcha.textproducer.char.string

圖片內容從這些字元中產生

abcde2345678gfynmnpwx

kaptcha.textproducer.char.length

圖片內容的長度

5

kaptcha.textproducer.font.names

圖片字型名,用逗號隔開。

Arial, Courier

kaptcha.textproducer.font.size

圖片字型大小

40px

kaptcha.textproducer.font.color

圖片字型顏色,可選值為red,green,black等

black

kaptcha.textproducer.char.space

字元之間空格數

2

kaptcha.noise.impl

噪度生產者

com.google.code.kaptcha.impl.DefaultNoise

(可自定義,實現NoiseProducer介面即可)

kaptcha.noise.color

噪度顏色,可選值為red,green,black等

Black

kaptcha.obscurificator.impl

模糊度實現者

com.google.code.kaptcha.impl.WaterRipple (可自定義,實現GimpyEngine介面即可)

kaptcha.background.impl

背景實現者

com.google.code.kaptcha.impl.DefaultBackground(可自定義,實現BackgroundProducer介面即可)

kaptcha.background.clear.from

背景顏色開始值,可選值是red,green,blue等

Light grey

kaptcha.background.clear.to

背景顏色結束值,可選值是red,green,blue

White

kaptcha.word.impl

字型渲染實現者

com.google.code.kaptcha.text.impl.DefaultWordRenderer(可自定義,實現WordRenderer介面即可)

kaptcha.session.key

驗證碼內容生成後會放入HttpSession中,這是內容在session中的key值

KAPTCHA_SESSION_KEY (程式有bug,建議不要配,程式中可通過session.getAttribute(Constants.KAPTCHA_SESSION_KEY)來取)

kaptcha.session.date

驗證碼生成的日期會放入HttpSession中,這是日期在session中的key值

KAPTCHA_SESSION_DATE

5.    重新整理驗證碼

// 點選重新整理驗證碼

$(function(){

$('#id_captchaImage').click(function(){

 $(this).hide().attr('src','/kaptcha/captcha-image?'+ Math.floor(Math.random()*100)).fadeIn();

});

});