11 Replies Latest reply on Sep 19, 2007 2:23 AM by Alex Ka

    Seam Captcha configuration

    Stefan Daume Newbie

      Hi,

      its great that the Captcha stuff comes integrated in Seam. Unfortunately, I find it quite often very hard to read the output and it will require multiple tries. I know that JCaptcha provides a few ways to control the output. Does the Seam layer for JCaptcha offer some of those configuration options as well ?

      Many thanks,

      MacD

        • 1. Re: Seam Captcha configuration
          henrik lindberg Apprentice

          I am also interested in this. How can the captcha in seam be configured? Is there any documentation?

          • 3. Re: Seam Captcha configuration
            Alex Ka Novice

            While waiting for the team to provide a way to configure captcha I have elaborated a tweak that I want make sure is correct. I have subclassed CaptchaImage like so:

            @Startup
            @Scope(APPLICATION)
            @Name("mycaptchaimage")
            @Intercept(NEVER)
            @Install(precedence = BUILT_IN,
             classDependencies = "com.octo.captcha.service.image.ImageCaptchaService")
            public class MyCaptchaImage extends CaptchaImage {
            
             private ImageCaptchaService service;
            
             public static MyCaptchaImage instance() {
             if (!Contexts.isApplicationContextActive()) {
             throw new IllegalStateException("No application context active");
             }
             return (MyCaptchaImage) Contexts.getApplicationContext().get(MyCaptchaImage.class);
             }
            
             @Override
             public void create() {
             service = new DefaultManageableImageCaptchaService(
             new FastHashMapCaptchaStore(),
             new MyImageCaptchaEngine(),
             180,
             100000,
             75000);
             }
            
             @Override
             protected String getResourcePath() {
             return "/mycaptcha";
             }
            
             @Override
             public boolean validateResponse(String id, String response) {
             try {
             return service.validateResponseForID(id, response);
             }
             catch (CaptchaServiceException cse) {
             return false;
             }
             }
            
             @Override
             public void getResource(HttpServletRequest request, HttpServletResponse response) throws IOException {
             ByteArrayOutputStream out = new ByteArrayOutputStream();
            
             try {
             Lifecycle.beginRequest(getServletContext(), request.getSession(), request);
            
             String captchaId = request.getQueryString();
            
             BufferedImage challenge = service.getImageChallengeForID(captchaId, request.getLocale());
            
             ImageIO.write(challenge, "jpeg", out);
             }
             catch (IllegalArgumentException e) {
             response.sendError(HttpServletResponse.SC_NOT_FOUND);
             return;
             }
             catch (CaptchaServiceException e) {
             response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
             return;
             }
             finally {
             Lifecycle.endRequest();
             }
            
             response.setHeader("Cache-Control", "no-store");
             response.setHeader("Pragma", "no-cache");
             response.setDateHeader("Expires", 0);
             response.setContentType("image/jpeg");
             response.getOutputStream().write(out.toByteArray());
             response.getOutputStream().flush();
             response.getOutputStream().close();
             }
            
            }
            


            The only change in the usage is that now the resource path is /mycaptcha. I have tested it and is seems to work. Is it OK to subclass the component like this?

            • 4. Re: Seam Captcha configuration
              Alex Ka Novice

              Oops, the code I provided only displays a custom captcha image, it cannot perform the validation. You have to create a custom "captcha" component.

              • 5. Re: Seam Captcha configuration
                Martin Sznapka Newbie

                I configured captcha by myself inside CaptchaImage.java:

                 @Create
                 public void create()
                 {
                 DefaultManageableImageCaptchaService serviceTmp = new DefaultManageableImageCaptchaService();
                 DefaultGimpyEngine engine = (DefaultGimpyEngine) serviceTmp.getEngine();
                
                 CaptchaFactory[] factories = new CaptchaFactory[1];
                 factories[0] = new GimpyFactory(
                 new RandomWordGenerator("ABCDEFHIJKLMNOPRSTUVYZ"),
                 new ComposedWordToImage(
                 new RandomFontGenerator(new Integer(20), new Integer(20)),
                 new FunkyBackgroundGenerator(new Integer(160), new Integer(60)),
                 new RandomTextPaster(new Integer(4), new Integer(4), Color.BLACK)
                 )
                 );
                 engine.setFactories(factories);
                
                 service = serviceTmp;
                 }
                


                • 6. Re: Seam Captcha configuration
                  Caye Newbie

                  Actually, i have been trying to customize the captcha with seam 2.0. And what i did is the same as wise_guybg, with some changes. My code is this:

                  import java.awt.Color;
                  import java.awt.image.BufferedImage;
                  import java.io.ByteArrayOutputStream;
                  import java.io.IOException;
                  
                  import javax.imageio.ImageIO;
                  import javax.servlet.http.HttpServletRequest;
                  import javax.servlet.http.HttpServletResponse;
                  
                  import org.jboss.seam.ScopeType;
                  import org.jboss.seam.annotations.Install;
                  import org.jboss.seam.annotations.Name;
                  import org.jboss.seam.annotations.Scope;
                  import org.jboss.seam.annotations.Startup;
                  import org.jboss.seam.annotations.intercept.BypassInterceptors;
                  import org.jboss.seam.captcha.CaptchaImage;
                  import org.jboss.seam.contexts.Contexts;
                  import org.jboss.seam.contexts.ServletLifecycle;
                  
                  import com.octo.captcha.component.image.backgroundgenerator.UniColorBackgroundGenerator;
                  import com.octo.captcha.component.image.fontgenerator.RandomFontGenerator;
                  import com.octo.captcha.component.image.textpaster.RandomTextPaster;
                  import com.octo.captcha.component.image.wordtoimage.ComposedWordToImage;
                  import com.octo.captcha.component.word.wordgenerator.RandomWordGenerator;
                  import com.octo.captcha.engine.image.gimpy.BasicGimpyEngine;
                  import com.octo.captcha.image.gimpy.GimpyFactory;
                  import com.octo.captcha.service.CaptchaServiceException;
                  import com.octo.captcha.service.captchastore.FastHashMapCaptchaStore;
                  import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
                  import com.octo.captcha.service.image.ImageCaptchaService;
                  
                  @Startup
                  @Scope(ScopeType.APPLICATION)
                  @Name(value="org.jboss.seam.captcha.captchaImage")
                  @BypassInterceptors
                  @Install(precedence = Install.DEPLOYMENT,
                   classDependencies="com.octo.captcha.service.image.ImageCaptchaService")
                  public class CustomCaptcha extends CaptchaImage
                  {
                   private ImageCaptchaService service;
                  
                   public static CustomCaptcha instance() {
                   if (!Contexts.isApplicationContextActive()) {
                   throw new IllegalStateException("No application context active");
                   }
                   return (CustomCaptcha) Contexts.getApplicationContext().get(CustomCaptcha.class);
                   }
                  
                   @Override
                   public void create() {
                   System.out.println("generating captcha...");
                  
                   BasicGimpyEngine customCaptcha = new BasicGimpyEngine();
                   GimpyFactory factory = new GimpyFactory(new RandomWordGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), new ComposedWordToImage(new RandomFontGenerator(new Integer(15), new Integer(15)), new UniColorBackgroundGenerator(new Integer(150), new Integer(30)), new RandomTextPaster(new Integer(4), new Integer(7), Color.BLACK)));
                   GimpyFactory[] factories = {factory};
                   customCaptcha.setFactories(factories);
                  
                   service = new DefaultManageableImageCaptchaService(
                   new FastHashMapCaptchaStore(),
                   customCaptcha,
                   180,
                   120000,
                   75000);
                   }
                  
                   @Override
                   public String getResourcePath() {
                   return "/mycaptcha";
                   }
                  
                   @Override
                   public boolean validateResponse(String id, String response) {
                   try {
                   return service.validateResponseForID(id, response);
                   }
                   catch (CaptchaServiceException cse) {
                   return false;
                   }
                   }
                  
                   @Override
                   public void getResource(HttpServletRequest request, HttpServletResponse response) throws IOException
                   {
                   ByteArrayOutputStream out = new ByteArrayOutputStream();
                   ServletLifecycle.beginRequest(request);
                  
                   try {
                   String captchaId = request.getQueryString();
                   BufferedImage challenge = service.getImageChallengeForID(captchaId, request.getLocale());
                   ImageIO.write(challenge, "jpeg", out);
                   }
                   catch (IllegalArgumentException e) {
                   response.sendError(HttpServletResponse.SC_NOT_FOUND);
                   return;
                   }
                   catch (CaptchaServiceException e) {
                   response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                   return;
                   }
                   finally {
                   ServletLifecycle.endRequest(request);
                   }
                  
                   response.setHeader("Cache-Control", "no-store");
                   response.setHeader("Pragma", "no-cache");
                   response.setDateHeader("Expires", 0);
                   response.setContentType("image/jpeg");
                   response.getOutputStream().write(out.toByteArray());
                   response.getOutputStream().flush();
                   response.getOutputStream().close();
                   }
                  
                  }


                  The only difference with the seam one is the precedence of installation and the kind of captcha generated (the default gimpy one was too dificult to read sometimes).

                  But i am having problems in the registration page where i'm using this captcha (i'm using on it JSF and A4J). The error is this one:

                  18:52:42,007 WARN [lifecycle] executePhase(PROCESS_VALIDATIONS 3,com.sun.faces.context.FacesContextImpl@1903f07) threw exception
                  javax.faces.FacesException: /common/ui-elements/forms/captcha.xhtml @17,46 value="#{mycaptcha.response}": Target Unreachable, identifier 'mycaptcha' resolved to null
                   at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:108)
                   at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:251)
                   at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:117)
                   at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)
                   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                   at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:687)
                   at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:469)
                   at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:403)
                   at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:301)
                   at org.tuckey.web.filters.urlrewrite.NormalRewrittenUrl.doRewrite(NormalRewrittenUrl.java:195)
                   at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:159)
                   at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:141)
                   at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:90)
                   at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:395)
                   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                   at org.ajax4jsf.framework.ajax.xmlfilter.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:127)
                   at org.ajax4jsf.framework.ajax.xmlfilter.BaseFilter.doFilter(BaseFilter.java:277)
                   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                   at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
                   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                   at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
                   at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
                   at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:179)
                   at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:433)
                   at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
                   at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
                   at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
                   at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157)
                   at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                   at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:241)
                   at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
                   at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:580)
                   at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
                   at java.lang.Thread.run(Thread.java:595)
                  Caused by: javax.el.PropertyNotFoundException: /common/ui-elements/forms/captcha.xhtml @17,46 value="#{mycaptcha.response}": Target Unreachable, identifier 'mycaptcha' resolved to null
                   at com.sun.facelets.el.TagValueExpression.getType(TagValueExpression.java:62)
                   at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getConvertedValue(HtmlBasicInputRenderer.java:81)
                   at javax.faces.component.UIInput.getConvertedValue(UIInput.java:934)
                   at javax.faces.component.UIInput.validate(UIInput.java:860)
                   at javax.faces.component.UIInput.executeValidate(UIInput.java:1065)
                   at javax.faces.component.UIInput.processValidators(UIInput.java:666)
                   at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1033)
                   at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1033)
                   at javax.faces.component.UIForm.processValidators(UIForm.java:229)
                   at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1033)
                   at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:662)
                   at org.ajax4jsf.framework.ajax.AjaxViewRoot.access$201(AjaxViewRoot.java:53)
                   at org.ajax4jsf.framework.ajax.AjaxViewRoot$3.invokeRoot(AjaxViewRoot.java:315)
                   at org.ajax4jsf.framework.ajax.JsfOneOneInvoker.invokeOnRegionOrRoot(JsfOneOneInvoker.java:53)
                   at org.ajax4jsf.framework.ajax.AjaxContext.invokeOnRegionOrRoot(AjaxContext.java:191)
                   at org.ajax4jsf.framework.ajax.AjaxViewRoot.processValidators(AjaxViewRoot.java:329)
                   at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:100)
                   ... 37 more
                  


                  I would like to know if there is some way that the captcha wouldnt fail with a4jsf at the same time, or if some of you could guess where i am making the mistake.

                  Thanks in advance!

                  • 7. Re: Seam Captcha configuration
                    Alex Ka Novice

                    To quote myself:

                    Oops, the code I provided only displays a custom captcha image, it cannot perform the validation. You have to create a custom "captcha" component.


                    The captcha classes provided by Seam are tightly related so you have to create a new copy of all of them if you want to have a custom create method.


                    btw Maybe your idea could work too. I mean, overriding CaptchaImage and changing the precedence so that it comes created first. I don't think you should change the resource path though ("/mycaptcha"). I don't have Seam's source code at my disposal but I think that's what the error you are receiving is for.

                    • 8. Re: Seam Captcha configuration
                      Alex Ka Novice

                      FYI Notice that yesterday this was fixed for the 2.0.0.GA version:
                      http://jira.jboss.com/jira/browse/JBSEAM-865

                      • 9. Re: Seam Captcha configuration
                        Caye Newbie

                        Thanks wise_guybg! That was the error as you told, that i should replace the captcha instead of name it mycaptcha.

                        Now is working perfectly :D

                        • 10. Re: Seam Captcha configuration
                          Shane Bryzak Master

                          Configuring the captcha image is now supported in the CVS version of Seam. Check the security chapter of the reference docs for more info (or see the seamspace example).

                          http://jira.jboss.org/jira/browse/JBSEAM-865

                          • 11. Re: Seam Captcha configuration
                            Alex Ka Novice

                            Thanks Shane. Could you please check my comment in Jira if this can be released with CR1?