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();
});
});