JCAPTCHA integration with CAS

     To integrate JCAPTCHA with CAS some changes has need

     In the login-webflow.xml:

     1- Replace the action state bindAndValidate for this

<action-state id="bindAndValidate">
   <action bean="authenticationViaFormAction" />
   <transition on="success" to="captchaCheck" />
   <transition on="error" to="errorCount" />
</action-state>

     2- Replace the action state submit for this

<action-state id="submit">
   <action bean="authenticationViaFormAction" method="submit" />
   <transition on="warn" to="warn" />
   <transition on="success" to="sendTicketGrantingTicket" />
   <transition on="error" to="errorCount" />
</action-state>

     3- Add the new action states

<decision-state id="captchaCheck">
   <if test="${flowScope.count != null &amp;&amp; flowScope.count >= 3}" then="captchaValidate" else="submit"/>
</decision-state>
<action-state id="captchaValidate">
   <action bean="captchaValidateAction" />
   <transition on="success" to="submit" />
   <transition on="error" to="errorCount" />
</action-state>
<action-state id="errorCount">
   <action bean="captchaErrorCountAction" />
   <transition on="success" to="viewLoginForm" />
</action-state>

     The code for the action bean CaptchaErrorCountAction

import org.jasig.cas.web.support.WebUtils;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

public final class CaptchaErrorCountAction extends AbstractAction {

   protected Event doExecute(final RequestContext context) {
      int count;
      try {
         count = (Integer)context.getFlowScope().get("count");
      } catch (Exception e) {
         count=0;
      }

      count++;

      context.getFlowScope().put("count",count);

      return success();
   }
}

     The code for the action bean captchaValidateAction

package yourPackage;
 
import org.jasig.cas.web.support.WebUtils;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import com.octo.captcha.service.CaptchaServiceException;
import com.octo.captcha.service.image.ImageCaptchaService;

public final class CaptchaValidateAction extends AbstractAction {
   private ImageCaptchaService captchaService;
   private String captchaValidationParameter = "j_captcha_response";
   
   protected Event doExecute(final RequestContext context) {
      String captcha_response = context.getRequestParameters().get(captchaValidationParameter);
      boolean valid = false;

      if(captcha_response != null){
         String id = WebUtils.getHttpServletRequest(context).getSession().getId();
         if(id != null){
            try {
               valid = captchaService.validateResponseForID(id,captcha_response).booleanValue();
            } catch (CaptchaServiceException cse) {
            }
         }
      }

      if(valid){
         return success();
      }
      return error();
   }

   public void setCaptchaService(ImageCaptchaService captchaService) {
      this.captchaService = captchaService;
   }

   public void setCaptchaValidationParameter(String captchaValidationParameter) {
      this.captchaValidationParameter = captchaValidationParameter;
   }
}

     To generate the captcha image you must add a new controller to send the image in response to your image url

     The code for the controller that process the image url

import com.octo.captcha.service.image.ImageCaptchaService;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import java.io.ByteArrayOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class CaptchaImageCreateController implements Controller, InitializingBean {
   private ImageCaptchaService jcaptchaService;
   public CaptchaImageCreateController(){
   }
   public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception {
      byte captchaChallengeAsJpeg[] = null;
      ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
      String captchaId = request.getSession().getId();
      java.awt.image.BufferedImage challenge=jcaptchaService.getImageChallengeForID(captchaId,request.getLocale());
      JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream);

      jpegEncoder.encode(challenge);
      captchaChallengeAsJpeg = jpegOutputStream.toByteArray();response.setHeader("Cache-Control", "no-store");
      response.setHeader("Pragma", "no-cache");
      response.setDateHeader("Expires", 0L);
      response.setContentType("image/jpeg");
      ServletOutputStream responseOutputStream = response.getOutputStream();
      responseOutputStream.write(captchaChallengeAsJpeg);
      responseOutputStream.flush();
      responseOutputStream.close();
      return null;
   }
   public void setJcaptchaService(ImageCaptchaService jcaptchaService) {
      this.jcaptchaService = jcaptchaService;
   }
   public void afterPropertiesSet() throws Exception {
      if(jcaptchaService == null)
         throw new RuntimeException("Image captcha service wasn`t set!");
      else
         return;
   }
}

     You should configure this controller in cas-servlet.xml and map to the url that the controller will process add the following to the handlerMappingC bean in  

     the property mappings

<prop key="/captcha.htm">captchaImageCreateController</prop>

     /captcha.htm or whatever url that you want to set shoul be map in the web.xml by add the following.

<servlet-mapping>
   <servlet-name>cas</servlet-name>
   <url-pattern>/captcha.htm</url-pattern>
</servlet-mapping>

     The last step is the use of this url in our authentication page by add the following JSP code

<c:if test="${not empty count && count >= 3}">
   <img src = "captcha.htm">
   <input name = "j_captcha_response" type = "text">
</c:if>

     The configuration of the beans described here

<bean id="captchaErrorCountAction" class="your.path.CaptchaErrorCountAction"/>

<bean id="captchaValidateAction" class="your.path.CaptchaValidateAction"
   p:captchaService-ref="jcaptchaService"
   p:captchaValidationParameter="j_captcha_response"/>

<bean id="captchaImageCreateController"  class="your.path.CaptchaImageCreateController">
   <property name="jcaptchaService" ref="jcaptchaService"/>
</bean>

     The configuration of the bean jcaptchaService is bussiness of the developer, the following is an example

<bean id="jcaptchaService"  class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService"/>

     This integration for JCAPTCHA provide a way to present a captcha image to the users after a varius number of bad authentications request.

     In this example of configuration the number of bad authentications requests used to present the captcha challenge is 3, but you can configure it.