JCaptcha实施Captcha Test的策略非常多。比如:它提供了ImageCaptchaService策略接口,这是借助图片(文字)实施Captcha Test的重要接口。借助于它定义的getImageChallengeForId()方法,我们能够返回图片(文字)信息,与此同时,如果开发者将Captcha Test的输入结果和这次测试对应的Id一并提供给CaptchaService定义的validateResponseForId()方法则JCaptcha将真正能够辨认出操作目标资源的具体类型。
例子:
首先:使用了常用的DefaultManageableImageCaptchaService,实现了单例模式:
| package sample;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;
/**
*
* @author worldheart
*
*/
public class JCaptchaServiceSingleton {
private static ImageCaptchaService imageCaptchaService =
new DefaultManageableImageCaptchaService();
public static ImageCaptchaService getInstance(){
return imageCaptchaService;
}
}
其次:JImageCaptchaServlet的doGet方法
|
package sample;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.octo.captcha.service.CaptchaServiceException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
/**
*
* @author worldheart
*
*/
public class JImageCaptchaServlet extends HttpServlet {
private static final long serialVersionUID = -5573583398891222199L;
protected static final Log log = LogFactory.getLog(JImageCaptchaServlet.class);
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
}
protected void doGet(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws ServletException,
IOException {
byte[] captchaChallengeAsJpeg = null;
//存储Captcha Test使用的图片
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
//借助于HttpSession ID存储Captcha ID,开发者也可以借助于其他惟一值
String captchaId = httpServletRequest.getSession().getId();
//获得Captcha Test使用的图片内容
BufferedImage challenge = JCaptchaServiceSingleton.getInstance()
.getImageChallengeForID(captchaId, httpServletRequest.getLocale());
//输出JPEG格式
JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream);
jpegEncoder.encode(challenge);
} catch (IllegalArgumentException e) {
log.error(e);
httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
} catch (CaptchaServiceException e) {
log.error(e);
httpServletResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
//确保Captcha Test的安全性、可靠性
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
//输出JPEG图片
httpServletResponse.setContentType("image/jpeg");
ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
responseOutputStream.close();
}
}
然后:在web.xml中配置servlet
|
<servlet>
<servlet-name>jcaptcha</servlet-name>
<servlet-class>sample.JImageCaptchaServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>contacts</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>jcaptcha</servlet-name>
<url-pattern>/jcaptcha</url-pattern>
</servlet-mapping>
最后:编写客户端页面:
|
<%@page pageEncoding="GBK" contentType="text/html; charset=GBK" %>
<%@ page import="sample.JCaptchaServiceSingleton, com.octo.captcha.service.CaptchaServiceException" %>
<html>
<head>
<title>JCaptcha测试</title>
</head>
<body>
<h1>Captcha Test测试</h1>
<form action="jcaptcha.jsp" method="post">
<table width="95%" bgcolor="f8f8ff" border="0" cellspacing="0" cellpadding="5">
<tr>
<td alignment="right" width="30%">
请将图片中显示的内容填充到输入框中:
</td>
<td width="70%">
<img src="jcaptcha"/><br><br>
<input type="text" name="jcaptcharesponse" value="">
</td>
</tr>
</table>
<br>
<%
String jcaptcharesponse = request.getParameter("jcaptcharesponse");
if (jcaptcharesponse != null && jcaptcharesponse.trim().length() > 0) {
Boolean isResponseCorrect = Boolean.FALSE;
String captchaId = session.getId();
try {
isResponseCorrect =
JCaptchaServiceSingleton.getInstance().
validateResponseForID(captchaId, jcaptcharesponse);
if(isResponseCorrect.booleanValue()){
%>
<font color="green"><%= captchaId %>(HttpSession ID),
<%= jcaptcharesponse %>(jcaptcharesponse)成功通过Captcha Test!
</font><br><br>
<%
} else{
%>
<font color="red"><%= captchaId %>(HttpSession ID),
<%= jcaptcharesponse %>(jcaptcharesponse)未能通过Captcha Test!
</font><br><br>
<%
}
} catch (CaptchaServiceException e) {
;
}
}
%>
<input type="submit" value="提交" />
</form>
</body>
</html>
JCaptcha与spring的集成
首先:为了使的DefaultManageableImageCaptchaService能够享受到spring DI的乐趣,我们要改造先前提供的JImageCaptchaServlet
|
package sample;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.octo.captcha.service.image.ImageCaptchaService;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
/**
*
* @author worldheart
*
*/
public class JCaptchaImageController implements Controller, InitializingBean {
private ImageCaptchaService imageCaptchaService;
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.imageCaptchaService);
}
public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
HttpServletResponse response) throws Exception {
byte[] captchaChallengeAsJpeg = null;
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
//获得HttpSession的ID
String captchaId = httpServletRequest.getSession().getId();
BufferedImage challenge = this.imageCaptchaService.
getImageChallengeForID(captchaId, httpServletRequest.getLocale());
JPEGImageEncoder jpegEncoder =
JPEGCodec.createJPEGEncoder(jpegOutputStream);
jpegEncoder.encode(challenge);
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
//配置响应结果的头信息,以确保Captcha Test的安全性、可靠性
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
ServletOutputStream responseOutputStream =
response.getOutputStream();
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
responseOutputStream.close();
return null;
}
public ImageCaptchaService getImageCaptchaService() {
return imageCaptchaService;
}
public void setImageCaptchaService(ImageCaptchaService imageCaptchaService) {
this.imageCaptchaService = imageCaptchaService;
}
}
其次:在提共了controller后,我们需要配置:
|
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
....
<prop key="/captchaImage.htm"> captchaImageCreateController </prop>
</props>
</property>
</bean>
<bean id="captchaImageCreateController"
class="sample.JCaptchaImageController" >
<property name="imageCaptchaService" ref="imageCaptchaService"/>
</bean>
<bean id="imageCaptchaService" class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService" >
</bean>
如果访问/captchaImage.htm页面就会发现有彩色图片生成
其三.输入页面:
|
<%@page pageEncoding="GBK" contentType="text/html; charset=GBK" %>
<%@ include file="/WEB-INF/jsp/include.jsp" %>
<html>
<head><title>添加新的联系人</title></head>
<body>
<h1>添加联系人</h1>
<form method="post">
<table width="95%" bgcolor="f8f8ff" border="0" cellspacing="0" cellpadding="5">
<tr>
<td alignment="right" width="20%">姓名:</td>
<spring:bind path="webContact.name">
<td width="20%">
<input type="text" name="name" value="${status.value}"/>">
</td>
<td width="60%">
<font color="red"><c:out value="${status.errorMessage}"/></font>
</td>
</spring:bind>
</tr>
<tr>
<td alignment="right" width="20%">电子邮件:</td>
<spring:bind path="webContact.email">
<td width="20%">
<input type="text" name="email" value="${status.value}"/>">
</td>
<td width="60%">
<font color="red"><c:out value="${status.errorMessage}"/></font>
</td>
</spring:bind>
</tr>
<tr>
<td alignment="right" width="20%">请将图片中显示的内容填充到输入框中:</td>
<spring:bind path="webContact.captchaResponse">
<td width="20%">
<img src="../captchaImage.htm" />
<input type="text" name="captchaResponse" value="${status.value}"/>">
</td>
<td width="60%">
<font color="red"><c:out value="${status.errorMessage}"/></font>
</td>
</spring:bind>
</tr>
</table>
<br>
<spring:hasBindErrors name="webContact">
<b>请先修复所有的错误!>
</spring:hasBindErrors>
<br><br>
<input name="execute" type="submit" alignment="center" value="新增">
</form>
<a href="../hello.htm"/>">回到主页</a>
</body>
</html>
其四:修改add.jsp对应的WebContactAddController SimpleFormController
|
package sample.contact;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.view.RedirectView;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
/**
* Controller for adding a new contact.
*
* @author Ben Alex
* @version $Id: WebContactAddController.java 1496 2006-05-23 13:38:33Z benalex $
*/
public class WebContactAddController extends SimpleFormController {
//~ Instance fields ================================================================================================
private ContactManager contactManager;
//~ Methods ========================================================================================================
protected Object formBackingObject(HttpServletRequest request)
throws ServletException {
WebContact wc = new WebContact();
wc.setSessionId(request.getSession(false).getId());
return wc;
}
public ContactManager getContactManager() {
return contactManager;
}
public ModelAndView onSubmit(Object command) throws ServletException {
String name = ((WebContact) command).getName();
String email = ((WebContact) command).getEmail();
Contact contact = new Contact(name, email);
contactManager.create(contact);
return new ModelAndView(new RedirectView(getSuccessView()));
}
public void setContactManager(ContactManager contactManager) {
this.contactManager = contactManager;
}
}
其五;配置文件(acegi本身采用了WebContactValidator校验器完成add.jsp中表单数据的校验)
|
<bean id="addValidator" class="sample.contact.WebContactValidatorVersion2">
<property name="captchaService" ref="captchaService" />
</bean>
<bean id="secureAddForm" class="sample.contact.WebContactAddController">
...
<property name="validator"><ref bean="addValidator"/></property>
</bean>
<bean id="captchaServcie" class="sample.JCaptchaServiceProxy">
<property name="service" ref="imageCaptchaService" />
</bean>
那么WebContactValidate2的定义如下:
|
package sample.contact;
import org.acegisecurity.captcha.CaptchaServiceProxy;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
/**
* Validates {@link WebContact}.
*
* @author Ben Alex
* @version $Id: WebContactValidator.java 1740 2006-11-14 02:30:00 +0000 (星期二, 14 十一月 2006) benalex $
*/
public class WebContactValidatorVersion2 implements Validator {
private CaptchaServiceProxy captchaService;
public boolean supports(Class clazz) {
return clazz.equals(WebContact.class);
}
public void validate(Object obj, Errors errors) {
WebContact wc = (WebContact) obj;
if ((wc.getName() == null) || (wc.getName().length() < 3)
|| (wc.getName().length() > 50)) {
errors.rejectValue("name", "err.name", "Name 3-50 characters is required. *");
}
if ((wc.getEmail() == null) || (wc.getEmail().length() < 3)
|| (wc.getEmail().length() > 50)) {
errors.rejectValue("email", "err.email", "Email 3-50 characters is required. *");
}
if(wc.getCaptchaResponse() == null ||
"".equals(wc.getCaptchaResponse().trim())){
errors.rejectValue("captchaResponse", "err.captchaResponse",
"请重新完成Captcha Test!");
} else {
//实施Captcha Test
if(!this.captchaService.validateReponseForId(wc.getSessionId(),
wc.getCaptchaResponse().trim())){
errors.rejectValue("captchaResponse", "err.captchaResponse",
"请重新完成Captcha Test!");
}
}
}
public CaptchaServiceProxy getCaptchaService() {
return captchaService;
}
public void setCaptchaService(CaptchaServiceProxy captchaService) {
this.captchaService = captchaService;
}
}
其六:CaptchaServiceProxy的定义如下:
|
package sample;
import org.acegisecurity.captcha.CaptchaServiceProxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.octo.captcha.service.CaptchaService;
import com.octo.captcha.service.CaptchaServiceException;
/**
*
* @author worldheart
*
*/
public class JCaptchaServiceProxy implements CaptchaServiceProxy {
protected static final Log log = LogFactory.getLog(JCaptchaServiceProxy.class);
//将JCaptcha提供的CaptchaService集成进来
private CaptchaService service;
/**
* 实施Captcha Test
*/
public boolean validateReponseForId(String id, Object response) {
if (id == null || response == null || "".equals(id)) {
return false;
} else {
try {
boolean result = service.validateResponseForID(id, response).booleanValue();
if(result){
log.info(id + "(会话ID), " + response + "(captchaResponose)成功通过Captcha Test测试!");
} else{
log.info(id + "(会话ID), " + response + "(captchaResponose)未能通过Captcha Test测试!");
}
return result;
} catch (CaptchaServiceException e) {
return false;
}
}
}
public void setService(CaptchaService service) {
this.service = service;
}
public CaptchaService getService() {
return service;
}
}
JCaptcha与Acegi集成:
首先给出test页面:
|
<%@page pageEncoding="GBK" contentType="text/html; charset=GBK" %>
<%@ include file="/WEB-INF/jsp/include.jsp" %>
<html>
<head>
<title>实施基于JCaptcha的Captcha Test测试</title>
</head>
<body>
<h1>Captcha Test测试</h1>
<form action="" method="post">
<table width="95%" bgcolor="f8f8ff" border="0"
cellspacing="0" cellpadding="5">
<tr>
<td alignment="right" width="30%">
请将图片中显示的内容填充到输入框中:
</td>
<td width="70%">
<img src="captchaImage.htm"/><br><br>
<input type="text" name="j_captcha_response" value="">
</td>
</tr>
</table>
<br>
<input type="submit" value="提交" />
</form>
<a href=".htm"/>">主页</a>
</body>
</html>
其次:为了能够单独测试上述Captcha,还需要提供SimpleFormController
|
package sample;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
/**
*
* @author worldheart
*
*/
public class CaptchaFormController extends SimpleFormController {
protected ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
//实施Captcha Test前的HTTP请求对应的URL
String originalRequestUrl = request.getParameter("original_requestUrl");
//在实际企业应用中,开发者还可能需要从HttpServletRequest中
//抽取出CaptchaEntryPoint已经存放的各种参数,
//比如original_request_method、original_request_parameters、等等。
//最终,依据这些参数还原出在实施Captcha Test前的用户请求。
//在某种程度上,CaptchaEntryPoint扮演了SavedRequest的角色。
String redirectUrl = originalRequestUrl;
//为了能够单独进行Captcha Test测试,特别提供了这一URL
if(redirectUrl == null)
redirectUrl = "";
return new ModelAndView("redirect:" + redirectUrl);
}
protected Object formBackingObject(HttpServletRequest request)
throws Exception {
return new Object();
}
}
下面是他们的配置:
|
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
...
<prop key="/captcha.htm">captchaFormController</prop>
</props>
</property>
</bean>
<bean id="captchaFormController"
class="sample.CaptchaFormController" >
<property name="formView" value="captcha"/>
<property name="sessionForm" value="false"/>
</bean>
下面Acegi提供的Captcha集成真正要介入进来:
其一:FilterChainProxy的定义:
|
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,captchaValidationProcessingFilter,channelProcessingFilter,logoutFilter,authenticationProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
其二:HttpSessionContextIntegrationFilter的定义:
|
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
<property name="context" value="org.acegisecurity.captcha.CaptchaSecurityContextImpl" />
</bean>
其三:captchaValidationProcessingFilter过滤器的定义:
|
<bean id="captchaValidationProcessingFilter"
class="org.acegisecurity.captcha.CaptchaValidationProcessingFilter">
<property name="captchaService" ref="captchaService" />
<property name="captchaValidationParameter" value="j_captcha_response" />
</bean>
<bean id="captchaService" class="sample.JCaptchaServiceProxy">
<property name="service" ref="imageCaptchaService" />
</bean>
其四:ChannelProcessingFilter的定义:
|
<bean id="channelProcessingFilter" class="org.acegisecurity.securechannel.ChannelProcessingFilter">
<property name="channelDecisionManager" ref="channelDecisionManager" />
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/secure/debug.jsp=REQUIRES_CAPTCHA_ONCE_ABOVE_THRESOLD_REQUESTS
</value>
</property>
</bean> class="org.acegisecurity.captcha.TestOnceAfterMaxRequestsCaptchaChannelProcessor">
另一个版本的完整实例:
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /**=httpSessionContextIntegrationFilter,captchaValidationProcessingFilter,channelProcessingFilter,logoutFilter,authenticationProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
class="org.acegisecurity.providers.ProviderManager">
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/> class="org.springframework.cache.ehcache.EhCacheFactoryBean">
foobar anonymousUser,ROLE_ANONYMOUS
foobar
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
/acegilogin.jsp?login_error=1 / /j_acegi_security_check
/acegilogin.jsp
false
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /captcha.htm=ROLE_ANONYMOUS,ROLE_USER /captchaimage.htm=ROLE_ANONYMOUS,ROLE_USER /jcaptcha.jsp=ROLE_ANONYMOUS,ROLE_USER /jcaptcha=ROLE_ANONYMOUS,ROLE_USER /index.jsp=ROLE_ANONYMOUS,ROLE_USER /hello.htm=ROLE_ANONYMOUS,ROLE_USER /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER /**=ROLE_USER
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /secure/debug.jsp=REQUIRES_CAPTCHA_BELOW_AVERAGE_TIME_IN_MILLIS_REQUESTS
class="org.acegisecurity.securechannel.ChannelDecisionManagerImpl">
class="org.acegisecurity.captcha.TestOnceAfterMaxRequestsCaptchaChannelProcessor"> value="REQUIRES_CAPTCHA_ONCE_ABOVE_THRESOLD_REQUESTS" /> class="org.acegisecurity.captcha.AlwaysTestAfterTimeInMillisCaptchaChannelProcessor"> value="REQUIRES_CAPTCHA_AFTER_THRESOLD_IN_MILLIS" />
class="org.acegisecurity.captcha.AlwaysTestAfterMaxRequestsCaptchaChannelProcessor"> value="REQUIRES_CAPTCHA_ABOVE_THRESOLD_REQUESTS" />
class="org.acegisecurity.captcha.AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor"> value="REQUIRES_CAPTCHA_BELOW_AVERAGE_TIME_IN_MILLIS_REQUESTS" /> class="org.acegisecurity.captcha.CaptchaValidationProcessingFilter">
|
阅读(5766) | 评论(0) | 转发(0) |