├── .travis.yml
├── .gitignore
├── src
├── main
│ └── java
│ │ ├── com
│ │ └── google
│ │ │ └── common
│ │ │ └── annotations
│ │ │ └── VisibleForTesting.java
│ │ └── de
│ │ └── triology
│ │ └── recaptchav2java
│ │ ├── ReCaptchaException.java
│ │ ├── ReCaptchaJson.java
│ │ ├── ReCaptcha.java
│ │ ├── Http.java
│ │ └── ReCaptchaEndPoint.java
└── test
│ ├── java
│ └── de
│ │ └── triology
│ │ └── recaptchav2java
│ │ ├── HttpTest.java
│ │ ├── ReCaptchaTests.java
│ │ ├── ReCaptchaJsonTest.java
│ │ └── ReCaptchaTest.java
│ └── resources
│ └── logback-test.xml
├── LICENSE
├── .editorconfig
├── README.md
└── pom.xml
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk8
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Maven template
3 | target/
4 | pom.xml.tag
5 | pom.xml.releaseBackup
6 | pom.xml.versionsBackup
7 | pom.xml.next
8 | release.properties
9 | dependency-reduced-pom.xml
10 | buildNumber.properties
11 | .mvn/timing.properties
12 |
13 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
14 | !/.mvn/wrapper/maven-wrapper.jar
15 |
16 | ### JetBrains
17 | /TestDataLoader.iml
18 | *.iml
19 | /.idea/
20 |
--------------------------------------------------------------------------------
/src/main/java/com/google/common/annotations/VisibleForTesting.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2006 The Guava Authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | * in compliance with the License. You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License
10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | * or implied. See the License for the specific language governing permissions and limitations under
12 | * the License.
13 | */
14 | package com.google.common.annotations;
15 |
16 | /**
17 | * Annotates a program element that exists, or is more widely visible than otherwise necessary, only
18 | * for use in test code.
19 | *
20 | * @author Johannes Henkel
21 | */
22 | public @interface VisibleForTesting {
23 | }
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 TRIOLOGY GmbH
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | #
2 | # MIT License
3 | #
4 | # Copyright (c) 2017 TRIOLOGY GmbH
5 | #
6 | # Permission is hereby granted, free of charge, to any person obtaining a copy
7 | # of this software and associated documentation files (the "Software"), to deal
8 | # in the Software without restriction, including without limitation the rights
9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | # copies of the Software, and to permit persons to whom the Software is
11 | # furnished to do so, subject to the following conditions:
12 | #
13 | # The above copyright notice and this permission notice shall be included in all
14 | # copies or substantial portions of the Software.
15 | #
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | # SOFTWARE.
23 | #
24 |
25 | root = true
26 |
27 | [*]
28 | end_of_line = lf
29 | insert_final_newline = true
30 | charset = utf-8
31 | indent_style = space
32 | indent_size = 4
33 | trim_trailing_whitespace = true
34 | tab_width = 4
35 |
36 | [Makefile]
37 | indent_style = tab
38 | tab_width = 8
39 |
40 | # java properties must be saved in latin1
41 | [*.properties]
42 | charset = latin1
43 |
--------------------------------------------------------------------------------
/src/main/java/de/triology/recaptchav2java/ReCaptchaException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 TRIOLOGY GmbH
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package de.triology.recaptchav2java;
25 |
26 | /**
27 | * Indicates that something technically went wrong during captcha validation.
28 | */
29 | @SuppressWarnings("WeakerAccess") // This is the public API!
30 | public class ReCaptchaException extends RuntimeException {
31 |
32 | public ReCaptchaException(String message, Throwable cause) {
33 | super(message, cause);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/test/java/de/triology/recaptchav2java/HttpTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 TRIOLOGY GmbH
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package de.triology.recaptchav2java;
25 |
26 | import org.junit.Rule;
27 | import org.junit.Test;
28 | import org.junit.rules.ExpectedException;
29 |
30 | public class HttpTest {
31 |
32 | @Rule
33 | public ExpectedException expectedException = ExpectedException.none();
34 |
35 | @Test
36 | public void handlesInvalidUrl() throws Exception {
37 | expectedException.expect(ReCaptchaException.class);
38 | Http.post("not an URL!", "");
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
29 |
30 |
31 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/main/java/de/triology/recaptchav2java/ReCaptchaJson.java:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 TRIOLOGY GmbH
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package de.triology.recaptchav2java;
25 |
26 |
27 | import org.slf4j.Logger;
28 | import org.slf4j.LoggerFactory;
29 |
30 | import javax.json.Json;
31 | import javax.json.JsonObject;
32 | import javax.json.JsonReader;
33 | import javax.json.stream.JsonParsingException;
34 | import java.io.StringReader;
35 |
36 | /**
37 | * JSON-related logic for reCAPTCHA.
38 | */
39 | class ReCaptchaJson {
40 | private static final Logger LOG = LoggerFactory.getLogger(ReCaptchaJson.class);
41 |
42 | private String json;
43 |
44 | ReCaptchaJson(String json) {
45 | this.json = json;
46 | }
47 |
48 | boolean isSuccess() {
49 | try {
50 | return readJsonObject().getBoolean("success");
51 | } catch (JsonParsingException e) {
52 | LOG.warn("Error parsing JSON. Defensively returning false", e);
53 | return false;
54 | }
55 | }
56 |
57 | private JsonObject readJsonObject() {
58 | try (JsonReader reader = Json.createReader(new StringReader(json))) {
59 | return reader.readObject();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/de/triology/recaptchav2java/ReCaptchaTests.java:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 TRIOLOGY GmbH
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package de.triology.recaptchav2java;
25 |
26 | class ReCaptchaTests {
27 |
28 | private static final String BODY_TEMPLATE = "{\n"
29 | + " \"success\": %s,\n" // true|false
30 | + " \"challenge_ts\": \"2017-06-26T09:31:42+0200\",\n" // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
31 | + " \"hostname\": \"localhost\"" // the hostname of the site where the reCAPTCHA was solved
32 | + "%s" // optional error codes: ,"error-codes": [...]
33 | + "}";
34 |
35 | private static final String BODY_ERROR_CODES_TEMPLATE = ",\n\"error-codes\": [%s]";
36 |
37 | private ReCaptchaTests() {
38 | }
39 |
40 | static String createBodySuccess() {
41 | return String.format(BODY_TEMPLATE, "true", "");
42 | }
43 |
44 | static String createBodyFailure() {
45 | return String.format(BODY_TEMPLATE, "false",
46 | String.format(BODY_ERROR_CODES_TEMPLATE, "\"invalid-input-secret\", \"invalid-input-response\""));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/de/triology/recaptchav2java/ReCaptchaJsonTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 TRIOLOGY GmbH
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package de.triology.recaptchav2java;
25 |
26 | import org.junit.Test;
27 |
28 | import static com.google.common.truth.Truth.assertThat;
29 | import static de.triology.recaptchav2java.ReCaptchaTests.createBodyFailure;
30 | import static de.triology.recaptchav2java.ReCaptchaTests.createBodySuccess;
31 |
32 | public class ReCaptchaJsonTest {
33 | @Test
34 | public void isSuccessTrue() {
35 | assertThat(new ReCaptchaJson(createBodySuccess()).isSuccess()).isTrue();
36 | }
37 |
38 | @Test
39 | public void isSuccessFalse() {
40 | assertThat(new ReCaptchaJson(createBodyFailure()).isSuccess()).isFalse();
41 | }
42 |
43 | @Test
44 | public void canCopeWithWitespaces() {
45 | String jsonSuccessWithWithespaces = "{\n\n\"success\": \n true \n }";
46 | assertThat(new ReCaptchaJson(jsonSuccessWithWithespaces).isSuccess()).isTrue();
47 | }
48 |
49 | @Test
50 | public void handlesInvalidJson() {
51 | assertThat(new ReCaptchaJson("not json").isSuccess()).isFalse();
52 | }
53 |
54 | @Test
55 | public void handlesEmptyJson() {
56 | assertThat(new ReCaptchaJson("").isSuccess()).isFalse();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/de/triology/recaptchav2java/ReCaptcha.java:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 TRIOLOGY GmbH
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package de.triology.recaptchav2java;
25 |
26 | import com.google.common.annotations.VisibleForTesting;
27 |
28 | /**
29 | * Java abstraction for verifying a response token generate by Google's reCAPTCHA V2 client widget on server side.
30 | */
31 | @SuppressWarnings("WeakerAccess") // This is the public API!
32 | public class ReCaptcha {
33 | private final String secret;
34 |
35 | /**
36 | * Creates a new ReCaptcha service using a specific secret key.
37 | *
38 | * @param secret the server-side secret. Most not be {@code null}!
39 | */
40 | public ReCaptcha(String secret) {
41 | this.secret = secret;
42 | }
43 |
44 | /**
45 | * Validates a response token generate by Google's reCAPTCHA V2 client widget.
46 | *
47 | * @param captchaResponseToken the result of a captcha challenge.
48 | * @return {@code true} if the challenge was successful, otherwise {@code false}
49 | * @throws ReCaptchaException when something technically went wrong during captcha validation.
50 | */
51 | public boolean isValid(String captchaResponseToken) {
52 | return createReCaptchaEndPoint().verify(captchaResponseToken);
53 | }
54 |
55 | @VisibleForTesting
56 | protected ReCaptchaEndPoint createReCaptchaEndPoint() {
57 | return new ReCaptchaEndPoint(secret);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # reCAPTCHA-V2-jee7
2 | [](https://travis-ci.org/schnatterer/reCAPTCHA-V2-jee7)
3 | [](https://jitpack.io/#schnatterer/reCAPTCHA-V2-jee7)
4 |
5 | Java Bindings for reCAPTCHA V2. See [Verifying the user's response | reCAPTCHA | Google Developers](https://developers.google.com/recaptcha/docs/verify). Basing on JEE7's JAX-RS 2.0 Client API and JSON P 1.0.
6 | Same API as [triologygmbh/reCAPTCHA-V2-java](https://github.com/triologygmbh/reCAPTCHA-V2-java)
7 |
8 | ## Prerequisites
9 |
10 | * Get a public and a secret API key [from Google](http://www.google.com/recaptcha/admin).
11 | * Set up your client side to display the reCAPTCHA V2 widget, **containing the public API key**, as described [here](https://developers.google.com/recaptcha/docs/display).
12 | * Send the response token to your server.
13 |
14 | ## Usage
15 |
16 | Get it via [JitPack](https://jitpack.io/#triologygmbh/reCAPTCHA-V2-java), for example using maven.
17 |
18 | Add the following maven repository to your POM.xml
19 |
20 | ```xml
21 |
22 |
23 | jitpack.io
24 | https://jitpack.io
25 |
26 |
27 | ```
28 |
29 | Then add the actual dependency
30 |
31 | ```xml
32 |
33 | com.github.schnatterer
34 | reCAPTCHA-V2-jee7
35 | 1.0.0
36 |
37 | ```
38 |
39 | On your server:
40 | * Create a new instance of `ReCaptcha`,
41 | * passing the reCAPTCHA **private API key** (the one beloging to the public key used in your client code to generate the response).
42 | * Then validate the token sent by the client, you'll receive a boolean response.
43 |
44 | It's as simple as that.
45 |
46 | ````java
47 | String response = "03AEHxwuyM-dll21GpJuJ65tGT6SVEvQEO3tvLfyxbbgBCaSdOLRQBT4Py-jMjGxplhE1wo7nn7Y6zRNgqUufFTnYDdqzYDTupfZkgx0LppSC3_eBKkODMopBaSBeeGMlt_wzkqWes5tAo34t2LmS0fGdwsE_feGJ_NsrB29NsUNAO78FGyL5DpL7f8K5dnh9Q_6QiN5Qg0MapUEu2w30r-GOI7MfVDMF7qk7wDwbM8uZmoIMn8AenNVKsZY0yEP6ghGVTBhtFvBVaD6jiHXeKztnAX1oLAvPa0jh9sJe20Dwk4jtmuemWKLI";
48 | String secret = "sMSd8L8jlFrKGHdtbXePOphPfhJO_oA4A0sfvw0i";
49 |
50 | new ReCaptcha(secret).isValid(response);
51 | ````
52 |
53 | ### Error Handling
54 | In case there are not technical problems `Recaptcha.isValid()` always returns a boolean. Otherwise a `ReCaptchaException` is thrown.
55 | If you need insight into the underlying HTTP traffic you best set the log level of all loggers `de.triology.recaptchav2java` to `TRACE` using your favorite [SLF4J](https://www.slf4j.org/) implementation.
56 |
57 |
--------------------------------------------------------------------------------
/src/main/java/de/triology/recaptchav2java/Http.java:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 TRIOLOGY GmbH
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package de.triology.recaptchav2java;
25 |
26 | import org.slf4j.Logger;
27 | import org.slf4j.LoggerFactory;
28 |
29 | import javax.ws.rs.ProcessingException;
30 | import javax.ws.rs.client.Client;
31 | import javax.ws.rs.client.ClientBuilder;
32 | import javax.ws.rs.client.Entity;
33 | import java.util.function.Function;
34 |
35 | /**
36 | * HTTP-related logic for reCAPTCHA.
37 | */
38 | class Http {
39 | private static final Logger LOG = LoggerFactory.getLogger(Http.class);
40 | private Http() {}
41 |
42 | static String post(String url, String urlParameters) {
43 | try {
44 | return postWithExceptions(url, urlParameters);
45 | } catch (ProcessingException e){
46 | throw new ReCaptchaException("I/O error sending or receiving the response ", e);
47 | }
48 | }
49 |
50 | private static String postWithExceptions(String url, String urlParameters) {
51 | return withClient(url, client ->
52 | client.target(url)
53 | .request()
54 | .post(Entity.json(urlParameters))
55 | .readEntity(String.class));
56 | }
57 |
58 | private static String withClient(String url, Function runnable) {
59 | Client client = null;
60 | try {
61 | LOG.trace("Creating client {}", url);
62 | client = ClientBuilder.newClient();
63 | return runnable.apply(client);
64 | } finally {
65 | if (client != null) {
66 | LOG.trace("Closing client for url {}", url);
67 | client.close();
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/de/triology/recaptchav2java/ReCaptchaEndPoint.java:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 TRIOLOGY GmbH
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package de.triology.recaptchav2java;
25 |
26 | import com.google.common.annotations.VisibleForTesting;
27 | import org.slf4j.Logger;
28 | import org.slf4j.LoggerFactory;
29 |
30 | /**
31 | * Implements the connection to the reCAPTCHA V2 endpoint.
32 | * See https://developers.google.com/recaptcha/docs/verify
33 | */
34 | class ReCaptchaEndPoint {
35 | private static final Logger LOG = LoggerFactory.getLogger(ReCaptchaEndPoint.class);
36 |
37 | @VisibleForTesting
38 | static final String URL_PROTOCOL_HOST = "https://www.google.com";
39 | @VisibleForTesting
40 | static final String URL_QUERY_PART = "/recaptcha/api/siteverify";
41 | private static final String URL_ABSOLUTE = URL_PROTOCOL_HOST + URL_QUERY_PART;
42 |
43 | private static final String POST_PARAM_SECRET = "secret";
44 | private static final String POST_PARAM_TOKEN = "response";
45 | private final String secret;
46 |
47 | ReCaptchaEndPoint(String secret) {
48 | this.secret = secret;
49 | }
50 |
51 | boolean verify(String token) {
52 | LOG.debug("Verifying token {}", token);
53 | String response = Http.post(getAbsoluteUrl(), createUrlParameters(token));
54 | boolean success = new ReCaptchaJson(response).isSuccess();
55 | LOG.debug("Received response for token {}: success={}", token, success);
56 | return success;
57 | }
58 |
59 | private String createUrlParameters(String token) {
60 | return POST_PARAM_SECRET + "=" + secret + "&" +
61 | POST_PARAM_TOKEN + "=" + token;
62 | }
63 |
64 | @VisibleForTesting
65 | protected String getAbsoluteUrl() {
66 | return URL_ABSOLUTE;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/de/triology/recaptchav2java/ReCaptchaTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | *
4 | * Copyright (c) 2017 TRIOLOGY GmbH
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | * SOFTWARE.
23 | */
24 | package de.triology.recaptchav2java;
25 |
26 |
27 | import com.github.tomakehurst.wiremock.junit.WireMockRule;
28 | import org.junit.Ignore;
29 | import org.junit.Rule;
30 | import org.junit.Test;
31 | import org.junit.rules.ExpectedException;
32 |
33 | import static com.github.tomakehurst.wiremock.client.WireMock.*;
34 | import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
35 | import static com.google.common.truth.Truth.assertThat;
36 |
37 | import static de.triology.recaptchav2java.ReCaptchaTests.*;
38 | import static de.triology.recaptchav2java.ReCaptchaEndPoint.*;
39 |
40 | public class ReCaptchaTest {
41 |
42 | private static final String EXPECTED_RESPONSE_TOKEN = "SomeToken";
43 | private static final String EXPECTED_SECRET = "topSecret";
44 | private static final String IRRELEVANT_SECRET = "not relvant for this test";
45 |
46 | @Rule
47 | public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort());
48 |
49 | @Rule
50 | public ExpectedException expectedException = ExpectedException.none();
51 |
52 |
53 | @Test
54 | public void isValid() throws Exception {
55 | ReCaptcha recaptcha = mockResponseSuccess();
56 |
57 | boolean reCaptchaResponse = recaptcha.isValid(EXPECTED_RESPONSE_TOKEN);
58 |
59 | assertThat(reCaptchaResponse).isTrue();
60 | verifyParameters();
61 | }
62 |
63 | @Test
64 | public void isValidFalse() throws Exception {
65 | ReCaptcha recaptcha = mockResponseFailure();
66 |
67 | boolean reCaptchaResponse = recaptcha.isValid(EXPECTED_RESPONSE_TOKEN);
68 |
69 | assertThat(reCaptchaResponse).isFalse();
70 | verifyParameters();
71 | }
72 |
73 | @Test
74 | @Ignore // Results in 404, not in error receiving?!
75 | public void isValidTransportErrorReceiving() throws Exception {
76 | ReCaptcha recaptcha = mockErrorReceiving();
77 | expectedException.expect(ReCaptchaException.class);
78 | expectedException.expectMessage("receiving");
79 |
80 | recaptcha.isValid(EXPECTED_RESPONSE_TOKEN);
81 | }
82 |
83 | @Test
84 | public void isValidTransportErrorSending() throws Exception {
85 | expectedException.expect(ReCaptchaException.class);
86 | expectedException.expectMessage("sending");
87 |
88 | ReCaptcha reCaptcha = mockErrorSending();
89 |
90 | reCaptcha.isValid(EXPECTED_RESPONSE_TOKEN);
91 | }
92 |
93 | private void verifyParameters() {
94 | verify(postRequestedFor(urlEqualTo(URL_QUERY_PART))
95 | .withRequestBody(containing("secret="+ EXPECTED_SECRET))
96 | .withRequestBody(containing("response="+EXPECTED_RESPONSE_TOKEN))
97 | );
98 | }
99 |
100 | private ReCaptcha mockErrorReceiving() {
101 | wireMockRule.setGlobalFixedDelay(Integer.MAX_VALUE);
102 | return new ReCaptchaWireMock();
103 | }
104 |
105 | private ReCaptcha mockErrorSending() {
106 | return new ReCaptchaFailingToConnect();
107 | }
108 |
109 | private ReCaptcha mockResponseSuccess() {
110 | mockResponse(createBodySuccess());
111 | return new ReCaptchaWireMock();
112 | }
113 |
114 | private ReCaptchaWireMock mockResponseFailure() {
115 | mockResponse(createBodyFailure());
116 | return new ReCaptchaWireMock();
117 | }
118 |
119 | private void mockResponse(String body) {
120 | stubFor(post(URL_QUERY_PART)
121 | .willReturn(aResponse()
122 | .withHeader("Content-Type", "application/json")
123 | .withBody(body)
124 | .withStatus(200)));
125 | }
126 |
127 | private class ReCaptchaWireMock extends ReCaptcha {
128 | ReCaptchaWireMock() {
129 | // Overwritten in createReCaptchaEndPoint()
130 | super(IRRELEVANT_SECRET);
131 | }
132 |
133 | @Override
134 | protected ReCaptchaEndPoint createReCaptchaEndPoint() {
135 | return new ReCaptchaEndPoint(EXPECTED_SECRET) {
136 | @Override
137 | protected String getAbsoluteUrl() {
138 | return super.getAbsoluteUrl().replace(
139 | URL_PROTOCOL_HOST,
140 | String.format("http://localhost:%s", wireMockRule.port()));
141 | }
142 | };
143 | }
144 | }
145 |
146 | private static class ReCaptchaFailingToConnect extends ReCaptcha {
147 | ReCaptchaFailingToConnect() {
148 | // Overwritten in createReCaptchaEndPoint()
149 | super(IRRELEVANT_SECRET);
150 | }
151 |
152 | @Override
153 | protected ReCaptchaEndPoint createReCaptchaEndPoint() {
154 | // Secret is not needed, because request fails anyway
155 | return new ReCaptchaEndPoint(IRRELEVANT_SECRET) {
156 | @Override
157 | protected String getAbsoluteUrl() {
158 | return super.getAbsoluteUrl().replace(URL_PROTOCOL_HOST, "http://localhost:0");
159 | }
160 | };
161 | }
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
27 |
29 | 4.0.0
30 |
31 | de.triology.recaptchav2-java
32 | recaptchav2-java
33 | 1.0.1-SNAPSHOT
34 | jar
35 |
36 | recaptchav2-java
37 | https://github.com/triologygmbh/recaptchav2-java
38 |
39 |
40 | scm:git:https://github.com/triologygmbh/recaptchav2-java
41 | scm:git:https://github.com/triologygmbh/recaptchav2-java
42 | https://github.com/triologygmbh/recaptchav2-java.git
43 | HEAD
44 |
45 |
46 |
47 |
48 |
49 | maven-compiler-plugin
50 | 3.6.1
51 |
52 | 1.8
53 | 1.8
54 |
55 |
56 |
57 |
58 | org.apache.maven.plugins
59 | maven-release-plugin
60 | 2.5.3
61 |
62 |
63 | @{project.version}
64 |
65 |
66 |
67 |
68 | maven-deploy-plugin
69 | 2.8.2
70 |
71 |
72 | internal.repo::default::file://${project.build.directory}/release
73 |
74 |
75 |
76 |
77 |
78 | org.apache.maven.plugins
79 | maven-source-plugin
80 | 3.0.1
81 |
82 |
83 | attach-sources
84 |
85 | jar
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | com.mycila
94 | license-maven-plugin
95 | 3.0
96 |
97 | LICENSE
98 |
99 | **/VisibleForTesting.java
100 |
101 |
102 |
103 |
104 |
105 |
106 | check
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | org.slf4j
118 | slf4j-api
119 | 1.7.25
120 |
121 |
122 |
123 | javax.json
124 | javax.json-api
125 | 1.0
126 | provided
127 |
128 |
129 |
130 | javax.ws.rs
131 | javax.ws.rs-api
132 | 2.0
133 | provided
134 |
135 |
136 |
137 |
138 | junit
139 | junit
140 | 4.12
141 | test
142 |
143 |
144 | org.mockito
145 | mockito-core
146 | 2.8.47
147 |
148 |
149 | com.google.truth
150 | truth
151 | 0.34
152 | test
153 |
154 |
155 |
156 | com.github.tomakehurst
157 | wiremock
158 | 2.6.0
159 |
160 |
161 | ch.qos.logback
162 | logback-classic
163 | 1.2.3
164 | test
165 |
166 |
167 | org.glassfish.jersey.core
168 | jersey-client
169 | 2.5
170 | test
171 |
172 |
173 | org.glassfish
174 | javax.json
175 | 1.0.4
176 | test
177 |
178 |
179 |
180 |
--------------------------------------------------------------------------------