Struts integration

Start with download

struts-mailreader.zip

Introduction

This document is a tutorial that demonstrates how to integrate JCaptcha with a Struts based Web application using the Jcaptcha Struts module.
It Uses the struts-mailreader application bundled with the Struts distribution as the base web application to captcha-ize.

This tutorial is devided into 5 Steps.

  • Step 1 : Prerequisites
  • Step 2 : install and configure the jcaptcha struts plugin
  • Step 3 : Indentify the to be captchai-zed page flow
  • Step 4 : Short cut the identified actions with captcha action
  • Step 5 : Modify the input page

Prerequisites

Install the struts-mailreader sample application in your favorite J2EE container.
Just deploy the war or the exploded war into your favorite application container deploy directory.
Install the jcaptcha struts plugin

  • Copy the jcaptcha jar to the WEB-INF/lib dir of the struts-mailreader web-app : jcaptcha-all-$version.jar
  • Copy the jcaptcha third parties jars to the WEB-INF/lib dir of the struts-mailreader web-app : ehcache-1.0.jar
  • Add the following plugin declaration at the end of the struts config file
                        <plug-in className="com.octo.captcha.module.struts.CaptchaServicePlugin"/>
                        
          

(there's many settable property available, but none are required, this is detailed in the module documentation).

Indentify the to be captchai-zed page flow

We want to captcha-ize the registration page flow in order to avoid robot registration spam...
This page flow concerns the following elements in struts-mailreader struts-config-registration.xml (using wildcard!!) file :

       
 ...

 <!-- ========== Form Bean Definitions =================================== -->
  <form-beans>

    <!-- Registration form bean -->
    <form-bean      name="RegistrationForm"
                    type="org.apache.struts.webapp.example.RegistrationForm"/>

  </form-beans>

  <!-- ========== Global Forward Definitions ============================== -->
  <global-forwards>
    <forward   name="Registration"         path="/Registration.jsp"/>
  </global-forwards>

  <!-- ========== Action Mapping Definitions ============================== -->
  <action-mappings>

    <!-- Matches all edit actions (in this case, only user regstration) -->
    <action    path="/Edit*"
               type="org.apache.struts.webapp.example.Edit{1}Action"
               name="{1}Form"
              scope="request"
           validate="false">
      <forward name="success"              path="/{1}.jsp"/>
    </action>

    <!-- Matches all save actions (in this case, only user registration) -->
    <action    path="/Save*"
               type="org.apache.struts.webapp.example.Save{1}Action"
               name="{1}Form"
              scope="request"
              input="{1}"/>

  </action-mappings>


...

Short cut

So what we have to do next is to short cut this action (SaveRegistration.do) to introduce a jcaptcha challenge verification action before calling the 'real' action.
In order to do it, we will

  • create a new action called jcaptchaRegistration that will execute the 'real' action.
  • replace the 'real' action class in order to execute the jcapthca validation routine
  • Finaly add the render challenge action that will produce the image challenge to render.

The final configuration will look as follow :

               
                    <!-- Matches all edit actions (in this case, only user regstration) -->
                       <action    path="/Edit*"
                                  type="org.apache.struts.webapp.example.Edit{1}Action"
                                  name="{1}Form"
                                 scope="request"
                              validate="false">
                         <forward name="success"              path="/{1}.jsp"/>
                       </action>



                    <!-- Transform the wildCardAction to use the jcaptcha validation routine-->

                                  <action
                                      path="/SaveRegistration"
                                      type="com.octo.captcha.module.struts.VerifyCaptchaChallengeAction"
                                      name="RegistrationForm"
                                      scope="request"
                                      input="Registration"
                                      validate="false"
                                      >
                                      <forward name="success" path="/jcaptchaRegistration.do"/>

                                  </action>


                    <!-- Add the real action -->

                    <action
                        path="/jcaptchaRegistration"
                        type="org.apache.struts.webapp.example.SaveRegistrationAction"
                        name="RegistrationForm"
                        scope="request"
                        input="Registration"
                        >


                    </action>

                    <!-- add the render action-->
                    <action
                        path="/jcaptcha"
                        type="com.octo.captcha.module.struts.image.RenderImageCaptchaAction"
                        >


                    </action>

Modify the input jsp

  • adding the jcatcha taglib declaration
  • adding a jcaptcha:message tag to add the failed message
  • adding a jcaptcha:question tag to add the question
  • adding an jcaptcha text input form
  • adding an jcaptcha challenge

                        <%@ page contentType="text/html;charset=UTF-8" language="java" %>
                        <%@ taglib uri="/tags/app"    prefix="app" %>
                        <%@ taglib uri="/tags/struts-bean" prefix="bean" %>
                        <%@ taglib uri="/tags/struts-html" prefix="html" %>
                        <%@ taglib uri="/tags/struts-logic" prefix="logic" %>

                            <%-- Add the jcaptcha taglib--%>
                        <%@ taglib uri="jcaptcha" prefix="jcaptcha"%>

                        <logic:equal name="RegistrationForm" property="action"
                                    scope="request" value="Edit">
                          <app:checkLogon/>
                        </logic:equal>

                        <html:html>
                        <head>
                        <logic:equal name="RegistrationForm" property="action"
                                    scope="request" value="Create">
                          <title><bean:message key="registration.title.create"/></title>
                        </logic:equal>
                        <logic:equal name="RegistrationForm" property="action"
                                    scope="request" value="Edit">
                          <title><bean:message key="registration.title.edit"/></title>
                        </logic:equal>
                        <html:base/>
                        </head>
                        <body bgcolor="white">

                        <html:errors/>

                        <%-- Add the message tag--%>
                        <jcaptcha:message/>

                        <html:form action="/SaveRegistration" focus="username"
                                 onsubmit="return validateRegistrationForm(this);">
                        <html:hidden property="action"/>
                        <table border="0" width="100%">

                         <%-- Add the jcaptcha form part--%>


                          <tr>
                            <th align="right">
                             <jcaptcha:question/>:
                            </th>
                            <td align="left">

                                                <%-- Add the image--%>

                                                <img src="jcaptcha.do"/>
                        <br/>
                                                <%-- Add the input tag--%>

                                                <input type="text"  name="jcaptcha_response" />

                            </td>
                          </tr>




                          <tr>
                            <th align="right">
                              <bean:message key="prompt.username"/>:
                            </th>
                            <td align="left">
                              <logic:equal name="RegistrationForm" property="action"
                                          scope="request" value="Create">
                                <html:text property="username" size="16" maxlength="16"/>
                              </logic:equal>
                              <logic:equal name="RegistrationForm" property="action"
                                          scope="request" value="Edit">
                        <%--
                                <bean:write name="RegistrationForm" property="username"
                                           scope="request" filter="true"/>
                        --%>
                            <html:hidden property="username" write="true"/>
                              </logic:equal>
                            </td>
                          </tr>
...               

Remarks and best practices

As you may have noticed :

  • We added a statistics page and links to demonstrate the manageable service features
  • The captcha test is hackable by directly using the real action : Choose a wird action name in order to avoid direct call
  • Chaining action as we just do in this tutorial is not recommended : Write your own action, use the sources of the VerifyCaptchaChallengeAction
  • As you can see, the update profil page flow is also captch-ized : Be carrefull when you choose your page flow to captcha-ize, you may split a page flow to captch-ize just a part of it
  • Use the user mailing list if you have more questions.