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.