Skip to Content
Motive – A brief note:


First things first, so, what is a CAPTCHA?, you might ask. Most of us would have come across the band-pass filter like images when we sign up for a new account at Yahoo !.


Yes, you got it right!!!. A CAPTCHA is an automated test that can distinguish between machines and humans. It can generate and grade tests automatically that virtually accepts all humans and rejects all machines.


With bots posing severe threat, there are many instances where WebDynpro applications are exposed to the public for purpose of registration etc,. Under such circumstances, how to retaliate against the spam robots?. Here comes Password as graphic picture on a page to entry and Alexis`[Showing up the generated Image | Re: Read Session ID in WD].


During this attempt to solve the security problem,  I got a simple yet effective solution for External Library (another forum post), as a by-product. Though there exists the possibily to work with a A bit of (impractical) scripting for Web Dynpro for this dependency problem, it becomes quite complex (atleast for me) as one would need to work across several DC borders using entity references.

Setting up the platform:

To start with, we need all the required libraries. As JCAPTCHA has built-in references to other open source libraries, let`s get those libraries as well from the links specified below:

JCAPTCHA

Commons Collections

Commons Betwixt</li>
</ol>
Extract the zip files (available @ links 1,2,3) to get jars commons-betwixt-0.7.jar, jcaptcha-all-1.0-RC3.jar, commons-collections-3.2.jar and commons-collections-3.2.jar.


Libraries & External Library DC:

In the NWDS, navigate to ‘File’ –> ‘New’ –> ‘Development Component Project’ and choose ‘Local Development’. Then, select ‘My Components’ under ‘Local Development’ and click ‘Next’. You’ll get the ‘New Development Component’ wizard. Fill in the appropriate values (as shown below) and choose ‘External Library’ as the DC Type and click ‘Finish’ to create a new Library DC project.


image


Add the aforementioned jar files to this Library DC by simply copying and pasting them to the ‘libraries’ folder of the project.


image


Public Part declaration:

This is the an important step where in we define two public parts (with the same contents <–> libraries), with one of type ‘Compilation‘ and the other of type ‘Assembly‘ (In other words, one for Build-Time and another for Runtime). So, first, select one of the newly added jar files (under the Library Project –> ‘libraries’ folder) say, commons-betwixt-0.7.jar and right click to choose ‘Development Component’ –> ‘Add to Public Part’ from the context menu.


image

PP – Compilation:

In the ‘Add Public Part’ wizard, specify a valid name (see to it that the option ‘Provides an API for developing/compiling other DCs’ is checked!) and click ‘Finish’ to create a public part of type ‘Compilation‘.


image


PP – Assembly:


Now, right click the same jar file commons-betwixt-0.7.jar, and choose ‘Development Component’ –> ‘Add to Public Part’. In the ‘Pulic Part Editor’ window, click ‘New’ to create a new public part of type ‘Assembly‘ by selecting the option ‘Can be packaged into other build results (e.g. SDAs)’.


image


Similarly, add the other three jar files to the same public parts by repeating the following steps seperately for each jar file:

  1. Right click the jar and select ‘Development Component’ –> ‘Add to Public Part’ –> ExternalLibCompilation
  2. Right click the same jar and select ‘Development Component’ –> ‘Add to Public Part’ –> ExternalLibAssembly

Finally do a DC build by choosing ‘Development Component’ –> ‘Build’ in the right click context menu of the Library project. Now the public parts are ready with us and we may proceed further to see how these libraries (contained in the public parts) could be accessed from a WD DC.

WebDynpro DC & DC usage relations:


Create a WebDynpro DC (by following the same steps mentioned for External Library creation, except that the DC Type should be chosen as ‘WebDynpro’) and add both the External Library public parts to the WD DC as used DCs. Care must be taken to ensure that only the ‘Build Time‘ dependency is established. Hint: One need not manipulate the dependency types as we already have two pulic parts: one each for ‘Runtime‘ and ‘Build Time‘. Here are the steps to add the DC usage relations.

Choose ‘DC Metadata’ under your WD DC and navigate to ‘DC Definition’ –> ‘Used DCs’. Right click ‘Used DCs’ to add the DC usage relations. In the ‘Add Dependency’ wizard, choose ‘Local Development’ –> ‘My Components’ –> ‘jcaptcha/jars’ –> ‘Public Parts’. Select both the pulic parts ExternalLibCompilation and ExternalLibAssembly and check the dependency type ‘Build Time‘, and click ‘Finish’.


image
image
image


Finally, ‘Build‘ the WD DC, so that all the declared dependency relationships gets well established. Thus far we’ve seen the approach for solving the problems related to the usage of External Library DCs.

Caveat confector:

One should always remember this significant and valid point: ‘Use ONLY DC Builds to create the deployable archive if you work with assembly public parts.‘. Let me explain this clearly. We have added a public part of type ‘Assembly‘ to the WD DC and hence, the local create archive options are not supported. So, never use the function ‘Create archive‘ or ‘Deploy new archive and run‘ while working with ‘Assembly‘ type public parts. Instead, always use the DC ‘Build‘, after that DC ‘Deploy‘ and then simply ‘Run‘ the WD application.

Implementing JCAPTCHA/WD Application

Having discussed in detail, the procedure for invoking JCAPTCHA libraries from the WD DC, let`s dive into the actual application itself. Developing the application is very simple. In the WebDynpro perspective, switch to the ‘Navigator’ tab. Select the WD project and go to the folder ‘src’ to create a new logical folder structure ‘com.jcaptcha.util’. Add a new java class ‘CaptchaServiceSingleton.java’ under that and add the following code to it for instantiating a singleton captcha service.


public class CaptchaServiceSingleton
{
/* Instantiate a Singleton CaptchaService */
private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService();

public static ImageCaptchaService getInstance()
{
return instance;
}
}

Go back to ‘WebDynpro Explorer’ to create a Label, (for specifying a short text) Inputfield, (to take the user perceived input based on the CAPTCHA), an Image UI element (for displaying the generated image) and a Button (for verifying the CAPTCHA</b>) in the view. Add a method called createCaptcha() and invoke it from the wdDoInit() of the view.


Code for createCaptcha()

/* Specify a Captcha ID that will identify the generated 

  1. captcha. Note that same Captcha ID must be used to

  1. validate the  response as well .

  1. So, the request hashcode is a good candidate!.  

  1. As an alternative, the standard java ‘Random.nextLong()’ 

  1. can also be used as the Captcha ID.

*/


  wdContext.currentContextElement().setCaptchaID(  WDProtocolAdapter.getProtocolAdapter().getRequestObject().hashCode()+ “”);


/* Get the challenge for the specified ID */
      BufferedImage image = CaptchaServiceSingleton.getInstance().getImageChallengeForID( wdContext.currentContextElement().getCaptchaID(), WDClientUser.getLoggedInClientUser().getLocale());


/* Create a new file for storing the image */           
File outFile = new File( wdContext.currentContextElement().getCaptchaID() + “.jpg”);
          try
          {


/* Write the image to the created file */
               ImageIO.write( image, “jpg”,outFile);
          }
          catch (IOException e)
          {
               wdComponentAPI.getMessageManager().reportException(e.getLocalizedMessage(), true);
          }     


/* Get the URL for the generated image      */
            
          String fileURL = “http://localhost:53000/” + “jcaptcha/resources/” + wdContext.currentContextElement().getCaptchaID() + “.jpg”;


/* Instead of using hardcoded values for the hostname and

  1. port, you may use the WebDynpro service TaskBinder

  1. to get the hostname and the port

*/


/* Set the image URL to the attribute bound to the image UI element. */          
wdContext.currentContextElement().setEncryptedImage(fileURL);          

How to retrieve the generated image?:


Everything in the code looks pretty straight forward except for one point. +Why should the image be retrieved using a URL instead of using a plain Image name (outFile.getName()) ?. The answer is simple and I guess all of us know that. By default, WD Runtime does a look-up for the specified ‘Image’ in the folder ‘<b>../../../resources/<folder>/<projectName>/Components/<componentName>/</b>‘, whereas the CAPTCHA image gets generated under the root folder ‘+’. Then, how to access the images?. Solution: ‘Use HTTP Alias ‘. One may refer Creating an HTTP Alias in WAS for creating the alias (with Alias Name = ‘‘ ) used in the code above.


image


CAPTCHA verification:

How to check if the user perceived input matches with the generated CAPTCHA?‘. You can validate the input, by having the following code in the action handler associated with the Button.


public void onActionverify(com.sap.tc.webdynpro.progmodel.api.IWDCustomEvent wdEvent )
  {
    //@@begin onActionverify(ServerEvent)
/* Get the User Input code from the input field */
          String userResponse = wdContext.currentContextElement().getEnteredText();


          try
          {
/* Call the service method to check if the user input

  1. matches the generated code. Remember that for validation,   

  1. we need to use the same Captcha ID (that we specified

  1. during Captcha creation)

*/
               Boolean validate = CaptchaServiceSingleton.getInstance().validateResponseForID(wdContext.currentContextElement().getCaptchaID(),userResponse);
               if(validate.booleanValue())
               {
                    wdComponentAPI.getMessageManager().reportException(“Entered code is correct !!!”, true);
               }
               else
               {
                    wdComponentAPI.getMessageManager().reportException(“Entered code is Invalid. Please enter the correct code”, true);


/* Blank out the input field */                    
wdContext.currentContextElement().setEnteredText(“”);


/* If the entered code is wrong, generate a new Captcha*/
                    createCaptcha();
               }
          }
          catch (Exception e)
          {
               wdComponentAPI.getMessageManager().raiseException(e.getLocalizedMessage(), true);
          }
    //@@end
  }

!https://weblogs.sdn.sap.com/weblogs/images/62531/Error.jpg|height=325|alt=image|width=438|src=https://weblogs.sdn.sap.com/weblogs/images/62531/Error.jpg|border=0!

Points to be noted in CAPTCHA generation principles:

A new CAPTCHA has to be generated after every validation. i.e, let`s assume that the user entered code doesn’t match the generated code, then, for the immediately next validation session, we can’t use/show the same image, as CAPTCHA is destroyed after first validation. An exception (CaptchaServiceException) is thrown when the provided ID is no longer valid (non-existant/ destroyed/expired CAPCTHA). So, in such cases, there is no way to retrieve the response back from the CAPTCHA, as this is the way the JCAPTCHA core security feature has been designed.


public Boolean validateResponseForID(String ID, Object response)throws CaptchaServiceException
{
if (!store.hasCaptcha(ID))
{
throw new CaptchaServiceException("Invalid ID, could not validate!");
}
else
{
Boolean valid = store.getCaptcha(ID).validateResponse(response);

            store.removeCaptcha(ID);

return valid;
}
}


Further, there is a small pitfall in the CAPTCHA verification rules. The method validateResponseForID(String, Object) returns a case in-sensitive result, i.e, it treats both the LowerCase/UpperCase input character codes as one and the same.

To report this post you need to login first.

15 Comments

You must be Logged on to comment or reply to a post.

  1. Sebastian Kluger
    Hey mate,

    about which Version are we talking? As it seems, the one I work with does not offer a “External Library” development component. I think we are on Service Pack 9…
    Did anyone come up with an idea how to use a Jar in a Web Dynpro DC without being deleted every single time you build the project?

    THX

    (0) 
  2. Peter Mayringer
    Hi,

    I am still struggling with the Captcha external library. I am getting a runtime error:
    java.lang.NoClassDefFoundError: com/octo/captcha/service/image/ImageCaptchaService

    I followed your instructions step by step.
    I am using Netweaver 7.0 SP9. 

    Any help would be appreciated,
    Peter Mayringer

    (0) 
    1. Balakrishnan B Post author
      Peter,

      I’ve developed and tested this one in 7.0.08. Not sure with SP 9, but it has work as intended in this version as well. Just a check again!. Are you sure you didn’t use the option “Deploy new archive and run”?

      Bala

      (0) 
  3. Siegfried Zeilinger
    I am currently implementing this as well. My biggest concern is the licensing topic. Generally I think that you need to publish the WD code when you use the component (under GPL!) for public web site parts.

    Am I wrong or did you simply not care?

    As I am developing for a customer of mine, I need to point this out to them.

    Thank you
    Sigi

    (0) 
  4. Vincenzo Turco
    Hi, thanks for sharing
    however, how should one deploy the external library? On nw70 sp13 external library DCs cannot be deployed as such, you must wrap them somehow in a j2ee server component and then add the server component as used DC in the wd DC.
    (0) 
  5. Omar Palomino Sandoval
    Hi BalaKrishnan, your post were useful for me to understand the diferences between the compile and assembly public parts. When I tested this approach works “fine”, but not so different like adding the jars manualy in a META-INF/lib. The result is that the deploys take too long in proportion with the number and size of the jars you added to your project. (5MB in both cases, DC External Library and META-INF/lib)
    So, I just preffer the approach of a J2EE Library  DC Wrapper (A bit of (impractical) scripting for Web Dynpro) because it’s a robust approach, it makes no sense to me that I need to redeploy a common library again and again if you could create an DC External Library, Wrapped inside a J2EE Library DC, and deploy it once. The size of your project ear goes down dramatically, the same with deploy time. (In my case, from 5MB to 200 KB)
    This is just my personal opinion, I tested these three approaches and pick the one that made my development faster.
    Regards,
    Omar
    (0) 
  6. Felipe Forero Guzman
    Thank you, very helpful !!

    I would like to present a form to avoid the HTTP Alias, using the BufferedImage image generated by the CaptchaServiceSingleton. This code also avoids the use of “File outFile” and “ImageIO.write” for making a phisical file at the default folder.

    ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
    JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream);

    try{
         jpegEncoder.encode(image);
         byte[] captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
         IWDCachedWebResource cachedResource =null;
         String imgUrl = null;     
         byte[] file = captchaChallengeAsJpeg;
         if (file != null) {
              cachedResource = WDWebResource.getWebResource(file, WDWebResourceType.JPG_IMAGE);
              try {
                   imgUrl = cachedResource.getURL();
                        dContext.currentContextElement().setEncryptedImage(imgUrl);
              } catch (WDURLExceptione){
                     wdComponentAPI.getMessageManager().reportException(e.getLocalizedMessage(), true);
              }
         }
    }catch(IOException e){ }
    (code by Rafael de Luke)

    (0) 

Leave a Reply