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

    Seam Captcha configuration

    stefand

      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

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

          • 2. Re: Seam Captcha configuration
            wise_guybg
            • 3. Re: Seam Captcha configuration
              wise_guybg

              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
                wise_guybg

                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
                  msznapka

                  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

                    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
                      wise_guybg

                      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
                        wise_guybg

                        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

                          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

                            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
                              wise_guybg

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