reCAPTCHA v3 - przykład użycia w JavaScript z Framework-iem Spring
W tym artykule chcielibysmy pokazać w jaki sposób używać Google reCAPTCHA v3 w połączeniu JavaScript po stronie przeglądarki oraz Spring Frameworku po stronie backendu.
Artykuł składa się z 3 częsci:
- opisu przykładowej konfiguracji w panelu Google reCAPTCHA v3,
- przykładowy kod działający po stronie przeglądarki (front-end),
- przykładowy kod działający po stronie serwera (back-end).
Oficjalna dokumentacja dla reCAPTCHA v3 znajduje się tutaj.
Wykonaj nastepujące kroki
- konfiguracja Google reCAPTCHA:
- wejdź do panelu Google reCAPTCHA
https://www.google.com/recaptcha/admin#list - utwórz nową konfugurację,
- wprowadź wymagane dane (znajdują się one niżej na zrzucie ekranu):
- Label (wprowadź swoją nazwę konfiguracji),
- reCAPTCHA type ustaw jako v3,
- Domains zawiera
my-domain.com
, które należy zastąpić nazwą swojej domeny oraz dodatkowolocalhost
, aby móc testować aplikację w trakcie dewelopmentu,
- zaakceptuj regulamin i kliknij Submit,
- wejdź do panelu Google reCAPTCHA
- integracja przykładowych kodów z twoją stroną,
- skopiuj kody źródłowe znajdujące w dalszej częsci artykułu,
- skopiuj site key do pliku
index.html
(należy zamienić kluczsite_key_xxyyzz
w dwuch miejscach - parametr w url oraz zmienną w kodzie):<script src="https://www.google.com/recaptcha/api.js?render=site_key_xxyyzz"></script>
var PUBLIC_KEY = 'site_key_xxyyzz';
- skopiuj secret key do pliku
ReCAPTCHAv3Utils.java
:private static final String SECRET_KEY = "secret_key_xxyyzz";
- otwórz w przeglądarce swoją stronę i sprawdź czy wszystk odziała poprawnie.
Porady:
- nie udostępniaj swojego secret key!
- ustaw
double SCORES_LEVEL = 0.7
zgodnie ze swoimi wymaganiami (opis znajdziesz poniżej w kodzie),- czasami dobrze jest wykonać pewną akcję jeśli dojedzie do wykrycia zachowania, które wsjkazuję na użycie bota (przykładowo odczekaj 60 sekund lub poproś użytkownika o potwierdzenie swojej tożsamości np. swoim mailem lub nr. telefonu).
Przykładowa konfuguracja panelu Google reCAPTCHA v3
W tej sekcji zamieszczono przykłądową konfigurację użytą podczas tworzenia reCAPTCHA v3. Poniższy zrzut ekranu jest w języku angielskim ze względu na to, że sam panel nie ma tłumaczenia na język polski.
Wskazówka: nie zapomnij dodać domeny
localhost
jeśli chcesz dewelopować apliację lokalnie z użyciem reCAPTCHA.
Przykładowy kod JavaScript wykonywany w przeglądarce (Front-end)
W tej sekcji przedstawiono przykłąd użycia reCAPTCHA v3 dla scenariusza logowania użytkownika - kod JavaScript. Tekst reprezetujący akcję login
możemy zamienić na cokolwiek (znajduje się on w linijce zawierającej: ReCAPTCHAv3Utils.request('login', onSuccess, onError)
).
index.html
file:
// ONLINE-RUNNER:browser;
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<!-- klucz reCaptcha v3 można uzyskąć na stronie https://www.google.com/recaptcha/admin#list -->
<script src='https://www.google.com/recaptcha/api.js?render=site_key_xxyyzz&hl=pl'></script>
</head>
<body>
<script>
/*
panel reCaptcha v3:
https://www.google.com/recaptcha/admin#list
dokumentacja reCaptcha v3:
https://developers.google.com/recaptcha/docs/v3
*/
var ReCAPTCHAv3Utils = new function() {
// klucz reCaptcha v3 można uzyskąć na stronie https://www.google.com/recaptcha/admin#list
var PUBLIC_KEY = 'site_key_xxyyzz';
// Odpytuje Google reCAPTCHAv3 API w celu uzyskania tokenu.
//
// argumenty:
// action - możymy wstawić tutaj dowolny tekst nazywający naszą akcję
// Rzuć okiem na "Use case" stronie https://developers.google.com/recaptcha/docs/v3
// przykładowno można uzyć. homepage, login, social, e-commerce
// onSuccess i onError - to funkcje callback, które wykonywane są ajko rezultat zapytania
//
this.request = function(action, onSuccess, onError) {
if (window.grecaptcha) {
window.grecaptcha.ready(function() {
var config = {
action : action
};
try {
var query = window.grecaptcha.execute(PUBLIC_KEY, config);
if (onSuccess) {
query.then(onSuccess);
}
} catch (e) {
var message = e && e.message || 'Bład zapytania reCAPTCHA.';
if (onError) {
onError(message);
}
}
});
} else {
if (onError) {
onError('reCAPTCHA v3 nie została załadowana poprawnie.');
}
}
};
};
// Przykład użycia:
function loginUser(data) {
var onSuccess = function(token) {
data.token = token; // <------- przyznany token przez Google reCAPTCHA v3
$.ajax({
type: 'POST',
url: '/backend/login-user',
data: JSON.stringify(data),
contentType : 'application/json; charset=utf-8',
dataType : 'json',
success: function(data) {
alert('Odpowiedź: ' + data.message);
},
error: function(error) {
alert('Błąd zapytania!');
}
});
};
var onError = function(message) {
alert('Błąd: ' + message);
};
ReCAPTCHAv3Utils.request('login', onSuccess, onError);
}
loginUser({
username: 'John',
password: 'my-password-here'
});
</script>
</body>
</html>
Przykładowy kod Java wykonywany w frameworku Spring Framework (Back-end)
W tej sekcji zaprezentowano przykładowy kod w jezyku Java za pomocą, którego jesteśmy w stanie razem z frameworkiem Spring wykonać zapytanie do Google-owego API reCAPTCHA v3 w celu zweryfikowania zachowania użytkownika - czy jest to bot lub człowiek.
Poniżej przedstawiono kilka plików, które należy umieścić w swoim projekcie.
Plik pom.xml
(wymagana do dodania zależność dla maven-a):
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.10</version>
</dependency>
Plik UserController.java
:
package logic.controller_captcha;
import logic.util.LoggerUtil;
import org.apache.log4j.Logger;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import logic.controller_captcha.recaptchav3.ReCAPTCHAv3Exception;
import logic.controller_captcha.recaptchav3.ReCAPTCHAv3Utils;
import logic.controller_captcha.recaptchav3.ReCAPTCHAv3Response;
@Controller
public class UserController {
private final Logger logger = Logger.getLogger(getClass());
// w zakresie od 0.0 do 1.0
// 1.0 wskazuję na bardzo dobrą inteakcję rzczeczywistego użytkownika
// 0.0 wskazuje na 100%, że jest to zachwoanie typu bot
private final static double SCORES_LEVEL = 0.7;
@RequestMapping(value = "/backend/login-user", method = RequestMethod.POST)
@ResponseBody
public String loginUser(
HttpServletRequest request,
@RequestBody LoginUserRequest loginUserRequest
) {
String token = loginUserRequest.getToken();
String address = request.getRemoteAddr();
try {
ReCAPTCHAv3Response response = ReCAPTCHAv3Utils.request(token, address);
if (response.getSuccess()) {
if (response.getScore() > SCORES_LEVEL) {
// some login operation here ...
return "{\"message\": \"Logowanie zakończone sukcesem.\"}";
} else {
return "{\"message\": \"Wykryto dziwne zachowanie.\"}";
}
}
return "{\"message\": \"Adres lub token jest niepoprawny.\"}";
} catch (ReCAPTCHAv3Exception e) {
return "{\"message\": \"Błąd reCAPTCHA.\"}";
}
}
}
Plik LoginUserRequest.java
:
package logic.controller_captcha;
public class LoginUserRequest
{
private String token;
// wstaw tutaj pozostałe pola ...
public String getToken() {
return this.token;
}
public void setToken(String token) {
this.token = token;
}
}
Plik ReCAPTCHAv3Utils.java
:
package logic.controller_captcha.recaptchav3;
import org.codehaus.jackson.map.ObjectMapper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
/*
panel reCaptcha v3:
https://www.google.com/recaptcha/admin#list
dokumentacja reCaptcha v3:
https://developers.google.com/recaptcha/docs/v3
*/
public final class ReCAPTCHAv3Utils {
private static ObjectMapper mapper = new ObjectMapper();
// klucz do reCaptcha v3 można uzyskać na stronie https://www.google.com/recaptcha/admin#list
private static final String SECRET_KEY = "secret_key_xxyyzz";
private ReCAPTCHAv3Utils() {
// pusty konstruktor ...
}
private static String readStream(InputStream stream) throws IOException {
StringBuilder builder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
builder.append(line);
}
}
return builder.toString();
}
private static ReCAPTCHAv3Response readObject(InputStream stream) throws IOException {
String response = readStream(stream);
return mapper.readValue(response, ReCAPTCHAv3Response.class);
}
public static ReCAPTCHAv3Response request(String token, String ip) throws ReCAPTCHAv3Exception {
try {
URL url = new URL("https://www.google.com/recaptcha/api/siteverify?secret="
+ SECRET_KEY + "&response=" + token + "&remoteip=" + ip);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
connection.setRequestMethod("GET");
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
return readObject(connection.getInputStream());
} finally {
connection.disconnect();
}
} catch (Exception e) {
throw new ReCAPTCHAv3Exception("Błąd dla zapytania reCAPTCHA.", e);
}
}
}
Plik ReCAPTCHAv3Exception.java
:
package logic.controller_captcha.recaptchav3;
public class ReCAPTCHAv3Exception extends Throwable {
public ReCAPTCHAv3Exception(String message) {
super( message );
}
public ReCAPTCHAv3Exception(String message, Throwable cause) {
super( message, cause );
}
}
Plik ReCAPTCHAv3Response.java
:
package logic.controller_captcha.recaptchav3;
import org.codehaus.jackson.annotate.JsonProperty;
public class ReCAPTCHAv3Response {
@JsonProperty( "success" )
private boolean success;
@JsonProperty( "score" )
private double score;
@JsonProperty( "action" )
private String action;
@JsonProperty( "challenge_ts" )
private Object timestamp;
@JsonProperty( "hostname" )
private String hostname;
@JsonProperty( "error-codes" )
private Object errors;
public boolean getSuccess() {
return this.success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public double getScore() {
return this.score;
}
public void setScore(double score) {
this.score = score;
}
public String getAction() {
return this.action;
}
public void setAction(String action) {
this.action = action;
}
public String getHostname() {
return this.hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
}