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 && 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.