Axel Mendoza Pupo
2008-06-17 12:41:35 UTC
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
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 jcaptchaService;
private String captchaValidationParameter = "_captcha_parameter";
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 = jcaptchaService.validateResponseForID(id, response).booleanValue();
} catch (CaptchaServiceException cse) {
}
}
}
if(valid){
return success();
}
return error();
}
public void setCaptchaService(JCaptchaServiceProxy 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
{
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;
}
private ImageCaptchaService jcaptchaService;
}
//*******************************************************
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="captchaService"
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"/>
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
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 jcaptchaService;
private String captchaValidationParameter = "_captcha_parameter";
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 = jcaptchaService.validateResponseForID(id, response).booleanValue();
} catch (CaptchaServiceException cse) {
}
}
}
if(valid){
return success();
}
return error();
}
public void setCaptchaService(JCaptchaServiceProxy 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
{
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;
}
private ImageCaptchaService jcaptchaService;
}
//*******************************************************
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="captchaService"
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"/>