Introduction
The Grails ReCaptcha plugin adds Google ReCaptcha support to Grails applications, protecting forms from spam and abuse while letting real people through with ease.
The plugin provides two things:
-
a tag library, under the
recaptchanamespace, that renders the captcha widget and the script tags it needs, and -
a service,
RecaptchaService, that verifies the answer the user submitted against the ReCaptcha service.
To use the plugin you need a ReCaptcha account, available from google.com/recaptcha.
License
This plugin is released under the Apache License, Version 2.0
Acknowledgments
The plugin was originally written by Chad Johnston at Megatome Technologies, and builds on the
ideas of recaptcha4j.
Following are the plugin contributors:
Source code
The full source code for this plugin can be found on GitHub.
For issues, improvements or new features go to the plugin’s GitHub issues.
Usage
Installation
Add the plugin to the dependencies block of your build.gradle:
dependencies {
implementation 'io.github.gpc:grails-recaptcha:8.0.0-RC1'
}
Requirements:
-
Grails 8.0.0 or higher
-
Java 21 or higher
Previous versions
io.github.gpc:grails-recaptcha is a new artifact. Earlier releases were published as
org.grails.plugins:recaptcha through the Grails plugin repository rather than Maven Central: use
version 7.0.0 for Grails 7, 5.0.0 for Grails 5, 4.0.0 for Grails 4, and 3.2.0 for Grails 3.
Configuration
The plugin reads its configuration from the recaptcha block of your application’s
application.yml. The values match those used by the ReCaptcha service:
recaptcha:
publicKey: "your site key"
privateKey: "your secret key"
includeScript: true
includeNoScript: true
publicKey and privateKey are required — the plugin throws an IllegalArgumentException when
either is missing or empty.
includeScript controls whether the <script> tag ReCaptcha needs is emitted together with the
widget; see the tag library reference for overriding it per tag. includeNoScript
controls whether the <noscript> fallback markup is emitted.
Per-environment configuration
Any of the settings can be given per environment. Disabling the captcha during development is the most common case:
environments:
development:
recaptcha:
enabled: false
production:
recaptcha:
enabled: true
When enabled is false the tag library renders nothing and verifyAnswer returns true
without contacting Google, so no controller changes are needed to turn the captcha off.
Proxy server configuration
If your server reaches the ReCaptcha service through a proxy, configure it under recaptcha.proxy:
recaptcha:
proxy:
server: "" # IP or hostname of the proxy server
port: "" # Proxy server port, defaults to 80
username: "" # Optional, only if the proxy requires authentication
password: "" # Optional, only if the proxy requires authentication
Only server is required. port defaults to 80, and username and password are needed only
for an authenticating proxy.
Timeout configuration
If verification requests to Google are slow, the network timeouts can be changed independently of each other:
recaptcha:
timeoutConfig:
connectTimeout: 10000 # Timeout for establishing the connection, in ms. Defaults to 10000
readTimeout: 1000 # Timeout for waiting on the response, in ms. Defaults to 1000
Externalized configuration
See the Grails documentation for using externalized configuration files. As an alternative to
application.yml, the plugin falls back to a RecaptchaConfig Groovy script on the classpath:
recaptcha {
publicKey = ""
privateKey = ""
includeNoScript = true
includeScript = true
}
environments {
development {
recaptcha {
enabled = false
}
}
production {
recaptcha {
enabled = true
}
}
}
Tag library
All tags live in the recaptcha namespace.
<recaptcha:recaptcha>
Generates the HTML that displays the captcha. Supports the following attributes:
| Attribute | Description |
|---|---|
|
|
|
|
|
Any of the supported ReCaptcha language codes. |
|
Optional tabindex of the widget. |
|
|
|
Optional JavaScript function called when the user submits a successful response. |
|
Optional JavaScript function called when the successful response has expired. |
|
Set to |
See the ReCaptcha client guide for more details.
<recaptcha:script>
Renders the required <script> tag on its own. Combine it with includeScript=false, set either
globally or on the tag, to place the script elsewhere in your markup. Supports the lang
attribute.
This tag does not work in the <head> section of the page.
|
<recaptcha:recaptchaExplicit>
Generates the HTML for explicit display and rendering of the captcha. Supports the following attributes:
| Attribute | Description |
|---|---|
|
Any of the supported ReCaptcha language codes. |
|
The JavaScript function called once all dependencies have loaded. This function is normally responsible for rendering the captcha. Required. |
For more about explicit mode, see the ReCaptcha documentation.
<recaptcha:renderParameters>
Generates the JSON-like string used as the argument to grecaptcha.render(). Supports theme,
size, tabindex, type, successCallback and expiredCallback, with the same meanings as on
<recaptcha:recaptcha>.
<recaptcha:ifEnabled> / <recaptcha:ifDisabled>
Render their body when the captcha is, respectively, enabled or disabled in the configuration.
<recaptcha:ifFailed>
Renders its body when the previous verification failed. Useful for showing your own error message.
Verifying the answer
Inject RecaptchaService into your controller and call verifyAnswer:
recaptchaService.verifyAnswer(session, request.getRemoteAddr(), params)
It returns true or false. It also returns true when the plugin is disabled in the
configuration, which means you do not have to change your controller to turn the captcha off.
After a successful verification, call cleanUp(session) to release the resources the plugin
associated with the session. This is not strictly required, but skipping it leaves the ReCaptcha
state in memory until the session expires.
Examples
A runnable example application lives under
examples/app1 in the
plugin’s repository. The snippets below are taken from it.
Automatic rendering
The most common scenario. In your GSP:
<recaptcha:ifEnabled>
<recaptcha:recaptcha theme="dark"/>
</recaptcha:ifEnabled>
Leaving out theme defaults the captcha to the light theme.
Explicit rendering
<script type="text/javascript">
var onloadCallback = function () {
grecaptcha.render('html_element', <recaptcha:renderParameters theme="dark" type="audio" tabindex="2"/>);
};
</script>
<g:form action="myAction" method="post">
<recaptcha:ifEnabled>
<recaptcha:recaptchaExplicit loadCallback="onloadCallback"/>
<div id="html_element"></div>
</recaptcha:ifEnabled>
<g:submitButton name="submit"/>
</g:form>
For more about explicit mode, see the ReCaptcha documentation.
Separate script tag
Set includeScript to false, either on the tag as below or globally in the configuration:
<body>
<g:form action="validateNormal" method="post">
<recaptcha:ifEnabled>
<recaptcha:recaptcha includeScript="false"/>
</recaptcha:ifEnabled>
<g:submitButton name="submit"/>
</g:form>
<recaptcha:script/>
</body>
The <script src="https://www.google.com/recaptcha/api.js?" async defer></script> tag is then
emitted at the bottom of the document instead of immediately before the <div> holding the
captcha.
Customizing the language
Set lang on <recaptcha:recaptcha>, <recaptcha:recaptchaExplicit> or <recaptcha:script>. See
ReCaptcha language codes for the available
values.
Verifying user input
An abbreviated controller that verifies the captcha when a new user is saved:
import com.megatome.grails.RecaptchaService
class UserController {
RecaptchaService recaptchaService
def save() {
def user = new User(params)
// ...other validation...
def recaptchaOK = recaptchaService.verifyAnswer(session, request.getRemoteAddr(), params)
if (!user.hasErrors() && recaptchaOK && user.save()) {
recaptchaService.cleanUp(session)
// ...other account creation activities...
render(view: 'showConfirmation', model: [user: user])
} else {
render(view: 'create', model: [user: user])
}
}
}
Testing
Google publishes test keys that render a captcha which always validates. Configuring them in your test environment lets integration tests exercise the plugin without a real ReCaptcha account:
environments:
test:
recaptcha:
publicKey: '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'
privateKey: '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe'
To assert on the rendered markup without any network access at all, call the service directly:
def html = recaptchaService.createCaptcha(theme: 'dark')
expect:
html.contains('class="g-recaptcha"')
html.contains('data-theme="dark"')
Alternatively, disable the captcha entirely in the test environment — verifyAnswer then returns
true without contacting Google.
Release Notes
-
8.0.0
-
Support for Grails 8, Spring Boot 4 and Java 21
-
Migrated to the
grails-plugin-templatebuild structure -
Published as
io.github.gpc:grails-recaptcha, replacingorg.grails.plugins:recaptcha
-
-
7.0.0 - Support for Grails 7
-
5.0.0 - Support for Grails 5
-
4.0.0 - Support for Grails 4
-
3.2.0 - Configurable network timeouts for captcha verification
-
3.1.0 - Support for Grails 3.2
-
3.0.1 - Removed Mailhide support;
sizeattribute supported in render parameters -
3.0.0 - Support for Grails 3