Invisible recaptcha to AEM

This post is about how to add invisible google recaptcha to your AEM site.

Get Started

First of all you need to register for keys

Go to: https://www.google.com/recaptcha/admin#list

keys

you will get secret key and site key that’s all we need from here. Select invisible and also a number of domains you can register for s single pair of keys.

Create recaptcha component

It depends on your requirement but I advise to include in form container.

Just create a recaptcha.jsp or html and include in form container so that it will be a part of form when you submit form data.

CaptchaHtmlYou should make site key configurable and get it here.

Below attribute will make recaptcha hidden if you remove it then checkbox will appear so thats upto you.

data-size="invisible"

Verify recaptcha response at server side

When you submit the form you will get the parameter g-recaptcha-response and all that is needed to check if captcha is valid.

public boolean isCaptchaValid(SlingHttpServletRequest request, SlingHttpServletResponse response) {
   try {
      final String postParameters = "secret=" + this.config.secretKey() + "&response="
            + getCaptchaResponse(request);
      final String message = proxyHttpConnectionProvider.doPostRequest(this.config.url() , postParameters);
      
      if (StringUtils.isEmpty(message)) {
         return false;
      }
      
      if(message.contains("true")) {
         return true;
      }
      return false;
   } catch (Exception e) {
      return false;
   }
}
private String getCaptchaResponse(SlingHttpServletRequest request) {
   Map<String, String[]> parameterMap = request.getParameterMap();
   for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
      if (param.getKey().equals("g-recaptcha-response")) {
         Object value = param.getValue();
         if (value instanceof String[]) {
            String[] valueArr = (String[]) value;
                   return valueArr[0];
         } else {
            return (String) value;
         }
      }
   }
   return null;
}

All you need to do is make a post request with below details. (This post request can differ as per your project Here I have used proxy settings.)

API Request

URL: https://www.google.com/recaptcha/api/siteverify

METHOD: POST

POST Parameter Description
secret Required. The shared key between your site and reCAPTCHA.
response Required. The user response token provided by reCAPTCHA, verifying the user on your site.
remoteip Optional. The user’s IP address.

API Response

The response is a JSON object:

{
  "success": true|false,
  "challenge_ts": timestamp,  // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
  "hostname": string,         // the hostname of the site where the reCAPTCHA was solved
  "error-codes": [...]        // optional
}

AEM Best Code Practices

AEM related (Sightly, OSGi components, Sling models)

Sightly

  • Use of Sightly must be preferred over JSP
  • Avoid injecting CSS code in HTML Sightly templates
  • Use unsafe context sparingly
  • Use tag instead of data-sly-unwrap attribute (deprecated)
  • We should write a Sling Model to handle links handling
    • e.g. adding .html extension
  • Get rid of the custom TransformerFactory
  • Use data-sly-attribute.attributeName only when necessary (it is for removing the attribute from the element if the condition is false).
    Otherwise use normal html attributes.
  • ${variable != “”} can be replaced with ${variable}
  • Use component name when including the back-end file.
    • e.g. “data-sly-use.footer”
  • Don’t put images or other containers on the page when the data is empty (check with data-sly-test, it puts the container on the page only when the values exist).
  • If there is no logic around some property values (just a getter for the field), don’t access them through Java/Sling models. Use ${properties.propertyName} instead to get values directly from JCR when all you need is the unmodified value.

Sling Models

  • Prefer usage of Sling Models over Java Use API or Javascript Use API
  • Adapt from Request only when you really need, otherwise Resource
  • AEM components business logic implementation: avoid using WCMUsePojo. Use Sling Models instead.
  • Catch exceptions inside components business logic implementation
  • Use Sling models instead of WCMUsePojo
  • Use Optional as default injection strategy
    • @Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
    • Eliminates the need to put @Optional on each field separately
  • Getting the property values from JCR
    • Use @ValueMapValue annotation on a field with the same name as the property
    • If there is no logic around some property values (just a getter for the field), don’t access them through Java/Sling models
    • Use ${properties.propertyName} in HTML/Sightly instead to get values directly from JCR

Sling Servlets

  • Register Sling Servlets with resource type and not with resource paths

Commons

  • Avoid hard coding content structure

Node configNode = session.getNode(“/content/project/folder”);

  • Avoid hard coding of configurations
  • Avoid memory leaks by closing JCR Session and Sling Resource Resolver (Use Java 7 feature try-with-resource on ResourceResolver object)
  • Write operations on the repository should not be taken too easy
  • When doping large write repository operations, do a session.save() approx. every 1000 nodes
  • Avoid JCR queries whenever possible
  • Avoid unnecessary JCR observations, restrict as much as possible the scope of listeners
  • Avoid writing to the repository in JCR Observation or Sling event listeners. Collect the data and write outside of these handlers in a separate thread.
  • Do not use deprecated features, especially administrative ResourceResolver (use specific service user)
  • Avoid adding new dependencies, avoid embedding new compile jar dependencies
  • Always prefer working on Sling resources instead of JCR nodes/properties
  • Use Sling Resource and ValueMap API to read data from JCR
  • Implement proper abstractions (using adaptTo pattern) for String operations on paths in order to avoid excessive use of String operations

String[] segments = resource.getPath().split(“/”);
String settingsPath = “/” + StringUtils.join(segments,”/”,0,2) + “/settings/jcr:content”;
Resource settings = resourceResolver.get(settingsPath);
ValueMap vm = settings.adaptTo(ValueMap.class);
String language = vm.get(“language”);

vs

String language = “en”;
Settings settings = resource.adaptTo(Settings.class);
If (settings != null) {
language = settings.getLanguage();
}

  • Write adaptTo() calls with proper null checks or use ModelFactory instead!
    Log hygiene, avoid excessive use of LOGGER.error(); meaningful usage of error, warn, info, debug levels
  • Develop components with dispatcher in mind (caching and invalidation)
  • If we start using link rewriter then we need to use resourceResolver.map() for mapping links
  • error.log must be kept clean of errors/warnings
  • Use consistent naming conventions across all AEM components implementations (dialog input field name and descriptions, AEM content folder structure)
  • Use overrides as less possible, prefer extending a component with sling:resourceSuperType

OSGi components