Hi,
I wanted to customise the Login screen to include CAPTCHA. How do I go about doing this ?
Thanks
Gayathri
Hi,
I wanted to customise the Login screen to include CAPTCHA. How do I go about doing this ?
Thanks
Gayathri
Hi Gyathri;
I've never worked with any CAPTCHA service, so I don't know precisely about their integration.
However, the login process is taken care of by the frontend controller, and the API should be open enough for you to bring in your customizations.
First of all, the controller is injected with a view (loginView property) that describes the login dialog content. The default implementation is defined here (look for the "loginViewDescriptor" bean definition). you can see that it simply defines a form with 3 fields (username / password / rememberMe). In your case, you would need to add a read-only field for the CAPTCHA question (either text or image), and another one for the answer.
Afterwards, you need to extend the model of the login dialog to include the 2 new fields, as well as control the CAPTCHA challenge before actually trying to login.
You would basically have to subclass org.jspresso.framework.application.frontend.controller.remote.DefaultRemoteController in order to override some useful protected methods :
HTH,
Vincent
Hi Vincent,
Thank you for your response. I will try it.
Thanks
Gayathri
Hi Vincent,
I was trying to add the CAPTCHA image to the login view descriptor. I tried to use BasicImageViewDescriptor. However I am getting errors when I do this. What should I use to display the image generated ??
Thanks
Gayathri
Hi Gayathri,
What you should do :
This is for instance what's done on HRSample to display the genderImageUrl property :
model.groovy
string_512 'genderImageUrl', id:'Employee-genderImageUrl', readOnly:true, computed:true
view.groovy
image model:'Employee-genderImageUrl', scrollable:false
EmployeeExtension
/**
* Returns the gender image url.
*
* @return the gender image url.
*/
public String getGenderImageUrl() {
IEnumerationPropertyDescriptor genderDescriptor = (IEnumerationPropertyDescriptor) getComponentFactory()
.getComponentDescriptor(Employee.class).getPropertyDescriptor("gender");
return genderDescriptor.getIconImageURL(getComponent().getGender());
}
You should be able to apply the same design to display the CAPTCHA. Of course, if you define a captchaImageUrl property, you should define it on your UsernamPasswordHandler subclass, since it is the model of your login view.
Hi Vincent,
Thank you for your response. I use XML. Would you mind telling me whats wrong with the way I have declared -
<bean id="loginViewDescriptor" class="org.jspresso.framework.view.descriptor.basic.BasicComponentViewDescriptor">
<property name="name" value="login.name" />
<property name="iconImageURL" value="classpath:org/jspresso/framework/application/images/password-48x48.png" />
<property name="columnCount" value="1" />
<property
name="propertyViewDescriptors">
<list>
<bean
class="org.jspresso.framework.view.descriptor.basic.BasicPropertyViewDescriptor">
<property
name="name"
value="username" />
</bean>
<bean
class="org.jspresso.framework.view.descriptor.basic.BasicPropertyViewDescriptor">
<property
name="name"
value="password" />
</bean>
<bean
class="org.jspresso.framework.view.descriptor.basic.BasicPropertyViewDescriptor">
<property
name="name"
value="rememberMe" />
</bean>
<bean
class="org.jspresso.framework.view.descriptor.basic.BasicImageViewDescriptor">
<property
name="name"
value="captchaStringURL" />
</bean>
</list>
</property>
<property name="modelDescriptor">
<bean class="org.jspresso.framework.model.descriptor.basic.BasicComponentDescriptor">
<constructor-arg value="com.riapp.security.MyUsernamePasswordHandler" />
<property name="propertyDescriptors">
<list>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicStringPropertyDescriptor">
<property name="name" value="username" />
<property name="maxLength" value="64" />
</bean>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicPasswordPropertyDescriptor">
<property name="name" value="password" />
<property name="maxLength" value="64" />
</bean>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicBooleanPropertyDescriptor">
<property name="name" value="rememberMe" />
</bean>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicStringPropertyDescriptor">
<property name="name" value="captchaStringURL" />
<property name="maxLength" value="20" />
<property name="defaultValue" value="captcha.jpg" />
<property name="readOnly" value="true" />
</bean>
</list>
</property>
</bean>
</property>
</bean>
I get the error -
Caused by: java.lang.IllegalStateException: Cannot convert value of type [org.jspresso.framework.view.descriptor.basic.BasicImageViewDescriptor] to required type [org.jspresso.framework.view.descriptor.IPropertyViewDescriptor] for property 'propertyViewDescriptors[3]': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:231)
at org.springframework.beans.TypeConverterDelegate.convertToTypedCollection(TypeConverterDelegate.java:520)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:173)
This is because BasicImageViewDescriptor is not a IPropertyViewDescriptor. How do I overlay image for the String property ? I am sure I am missing something here.
Thanks
Gayathri
Hi Gayathri,
Image views are not meant to be used inside component views (since they don't implement IPropertyViewDescriptor). I will see if we can easily turn this feature on.
As of now, you have to combine image views in composite views as a standalone view. For instance, you could use a border composite view, put the login for to the center and the captcha image to the south as an image view. Look at the HRSample sources to see how the genderImageUrl image view is composed into an Employee view.
HTH,
Vincent
Hi Vincent,
I am not able to do extension for the subclass of UserNamePasswordHandler because when I do
LoginExtension extends AbstractComponentExtension<MyUserNamePasswordHandler>, I get a compilation error - Bound mismatch - MyUserNamePasswordHandler is not a valid substitute for the bounded parameter. I am getting this because UserNamePasswordHandler is not a component. Please let me know if you have any ideas.
Thanks
Gayathri
Hi Gayathri,
Why would you need the extension mechanism ? You can directly write your methods (services, computed properties, ...) in the MyUserNamePasswordHandler class since it's a plain java class, as opposed to the Jspresso entities that are interfaces which are dynamically implemented by a common implementation (thus the need for extension points to the generic implementation). The only thing that you have to care about is to correctly fire property change events (especially for computed properties).
HTH,
Vincent
Hi Vincent,
Thanks for your reply.
I have not been able to get the image to display. I have tried several ways. The following is the snippet from front-end.xml. I am not using any logic to compute the URL for now. Just hardcoding. Still it is not working. Here is the snippet -
<bean id="login-captchaStringURL" class="org.jspresso.framework.model.descriptor.basic.BasicStringPropertyDescriptor">
<property name="name" value="captchaStringURL" />
<property name="maxLength" value="200" />
<property name="readOnly" value="true" />
<property name="defaultValue" value="stickyImg" />
</bean>
<bean id="login" class="org.jspresso.framework.model.descriptor.basic.BasicComponentDescriptor">
<constructor-arg value="com.riapp.security.MyUsernamePasswordHandler" />
<property name="propertyDescriptors">
<list>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicStringPropertyDescriptor">
<property name="name" value="username" />
<property name="maxLength" value="64" />
</bean>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicPasswordPropertyDescriptor">
<property name="name" value="password" />
<property name="maxLength" value="64" />
</bean>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicBooleanPropertyDescriptor">
<property name="name" value="rememberMe" />
</bean>
<ref local="login-captchaStringURL"/>
</list>
</property>
</bean>
<bean id="loginViewDescriptor" class="org.jspresso.framework.view.descriptor.basic.BasicBorderViewDescriptor">
<property name="modelDescriptor" ref="login" />
<property name="iconImageURL" value="classpath:org/jspresso/framework/application/images/password-48x48.png" />
<property name="southViewDescriptor" ref="loginSouthViewDescriptor"/>
<property
name="centerViewDescriptor" ref="loginCompViewDescriptor"/>
</bean>
<bean id="loginSouthViewDescriptor" class="org.jspresso.framework.view.descriptor.basic.BasicImageViewDescriptor">
<property
name="modelDescriptor" ref="login-captchaStringURL"/>
<property
name="scrollable" value="false" />
</bean>
<bean id="loginCompViewDescriptor" class="org.jspresso.framework.view.descriptor.basic.BasicComponentViewDescriptor">
<property name="name" value="login.name" />
<property name="columnCount" value="1" />
<property
name="propertyViewDescriptors">
<list>
<bean
class="org.jspresso.framework.view.descriptor.basic.BasicPropertyViewDescriptor">
<property
name="name"
value="username" />
</bean>
<bean
class="org.jspresso.framework.view.descriptor.basic.BasicPropertyViewDescriptor">
<property
name="name"
value="password" />
</bean>
<bean
class="org.jspresso.framework.view.descriptor.basic.BasicPropertyViewDescriptor">
<property
name="name"
value="rememberMe" />
</bean>
</list>
</property>
</bean>
I tried a sample html file with the following code and this displays the catpcha servlet generated image-
<html>
<body>
<img src="stickyImg" />
</body>
</html>
Any ideas ?? Also, when I see the flash output in the console, the image row shows it an UITextField when I expect it show an image.
Thanks
Gayathri
Hi Gayathri,
I've done the exercise on HRSample. Here it goes :
1/ Define a custom login handler class (it holds the 2 new properties):
/**
* A custom username password handler to demonstrate how login view can be
* augmented.
*
* @version $LastChangedRevision$
* @author Vincent Vandenschrick
*/
public class CaptchaUsernamePasswordHandler extends UsernamePasswordHandler {
private String captchaImageUrl;
private String captchaChallenge;
/**
* Gets the captchaImageUrl.
*
* @return the captchaImageUrl.
*/
public String getCaptchaImageUrl() {
return captchaImageUrl;
}
/**
* Sets the captchaImageUrl.
*
* @param captchaImageUrl
* the captchaImageUrl to set.
*/
public void setCaptchaImageUrl(String captchaImageUrl) {
this.captchaImageUrl = captchaImageUrl;
}
/**
* Gets the captchaChallenge.
*
* @return the captchaChallenge.
*/
public String getCaptchaChallenge() {
return captchaChallenge;
}
/**
* Sets the captchaChallenge.
*
* @param captchaChallenge
* the captchaChallenge to set.
*/
public void setCaptchaChallenge(String captchaChallenge) {
this.captchaChallenge = captchaChallenge;
}
}
2/Extend the frontend controller to make use of this new login handler :
/**
* A custom remote controller used to override the login process in order to
* introduce a CAPTCHA challenge.
*
* @version $LastChangedRevision$
* @author Vincent Vandenschrick
*/
public class CustomRemoteController extends DefaultRemoteController {
/**
* {@inheritDoc}
*/
@Override
protected UsernamePasswordHandler createUsernamePasswordHandler() {
CaptchaUsernamePasswordHandler cuph = new CaptchaUsernamePasswordHandler();
cuph.setCaptchaImageUrl("http://www.jspresso.org/files/logo.png");
return cuph;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean performLogin() {
if (getLoginContextName() != null) {
if (!"jspresso"
.equalsIgnoreCase(((CaptchaUsernamePasswordHandler) getLoginCallbackHandler())
.getCaptchaChallenge())) {
// Captcha challenge failed.
return false;
}
}
return super.performLogin();
}
}
Of course, we haven't bound to a real CAPTCHA service. We simply set the Jspresso logo as the CAPTCHA image in the createUsernamePasswordHandler method And we check that the user actually entered "jspresso" in the challenge answer text box before falling back to normal login process (overriden performLogin method).
3/ Install the new login model description by replacing the standard one :
In your project's frontend.xml (replace the actual package of the fully qualified name in bold below):
<bean id="loginModelDescriptor" class="org.jspresso.framework.model.descriptor.basic.BasicComponentDescriptor">
<constructor-arg value="org.jspresso.hrsample.ext.security.CaptchaUsernamePasswordHandler" />
<property name="propertyDescriptors">
<list>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicStringPropertyDescriptor">
<property name="name" value="username" />
<property name="maxLength" value="64" />
</bean>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicPasswordPropertyDescriptor">
<property name="name" value="password" />
<property name="maxLength" value="64" />
</bean>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicImageUrlPropertyDescriptor">
<property name="name" value="captchaImageUrl" />
</bean>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicStringPropertyDescriptor">
<property name="name" value="captchaChallenge" />
<property name="maxLength" value="64" />
</bean>
<bean class="org.jspresso.framework.model.descriptor.basic.BasicBooleanPropertyDescriptor">
<property name="name" value="rememberMe" />
</bean>
</list>
</property>
</bean>
4/ Install you customized remote frontend controller :
In your project's frontend.xml (replace the actual package of the fully qualified name in bold below):
<bean
id="remoteFrontController"
parent="abstractFrontController"
class="org.jspresso.hrsample.ext.frontend.remote.CustomRemoteController">
</bean>
After proper translation, here's what the login dialog looks like.

HTH,
Vincent
Hi Vincent,
Thank you so much for your detailed response. Thanks for the step by step explanation.
I did not know about BasicImageUrlPropertyDescriptor. I substituted that in mine.
Now it tries to display the image. But I am getting the SSL error -
SEVERE: Servlet.service() for servlet ResourceProviderServlet threw exception
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(Unknown Source)
at java.net.URL.openStream(Unknown Source)
at org.jspresso.framework.util.resources.server.ResourceProviderServlet.doGet(ResourceProviderServlet.java:322)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
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.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
at java.lang.Thread.run(Unknown Source)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
at sun.security.validator.Validator.validate(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
... 28 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
at java.security.cert.CertPathBuilder.build(Unknown Source)
... 34 more
I am looking into this too. If you have ideas, please let me know. Thanks a lot for taking so much effort in trying out the sample in HR application.
Thanks
Gayathri
Hi Gayathri,
Maybe you just need to install an extra certificate in your JVM ? See http://blogs.oracle.com/gc/entry/unable_to_find_valid_certification
HTH,
Vincent
Hi Vincent,
Thats great. Thank you for your link.
I wish I could tell everything is working fine. Unfortunately its not. In the custom controller class, if I put the jspresso logo image, it is displaying the logo beautifully in the login page. If I specify the path to the captcha servlet, it does not get the image. The custom controller code snippet is as follows -
protected UsernamePasswordHandler createUsernamePasswordHandler() {
MyUsernamePasswordHandler cuph = new MyUsernamePasswordHandler();
// cuph.setCaptchaStringURL( "https://localhost:8443/testapp-webapp/stickyImg");
cuph.setCaptchaImageUrl("http://localhost:8080/testapp-webapp/stickyImg");
//cuph.setCaptchaImageUrl("http://www.jspresso.org/files/logo.png");
return cuph;
}
I am attaching the login screen I am seeing. Thanks for all the help.
Thanks
Gayathri
| Attachment | Size |
|---|---|
| screenshotcaptcha.GIF | 77.36 KB |