JCaptcha生成验证码图片介绍

来源:百度文库 编辑:神马文学网 时间:2024/05/01 10:59:04

JCaptcha介绍


        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="hello.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 = "http://localhost:8080/contactsforchapter9/captcha.htm";
        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>