Registrácia s jarom - integrujte reCAPTCHA

1. Prehľad

V tomto tutoriále budeme pokračovať pridaním série Spring Security Registration GooglereCAPTCHA do procesu registrácie s cieľom odlíšiť človeka od robotov.

2. Integrácia reCAPTCHA spoločnosti Google

Aby sme integrovali webovú službu reCAPTCHA spoločnosti Google, je potrebné najskôr zaregistrovať náš web u tejto služby, pridať jej knižnicu na našu stránku a potom overiť odpoveď captcha používateľa pomocou tejto webovej služby.

Zaregistrujme si náš web na adrese //www.google.com/recaptcha/admin. Proces registrácie generuje a site-key a tajný kľúč pre prístup k webovej službe.

2.1. Ukladanie párov kľúčov API

Kľúče ukladáme do application.properties:

google.recaptcha.key.site = 6LfaHiITAAAA ... google.recaptcha.key.secret = 6LfaHiITAAAA ...

A vystavte ich Jarovi pomocou fazule s poznámkami @ConfigurationProperties:

@Component @ConfigurationProperties (prefix = "google.recaptcha.key") verejná trieda CaptchaSettings {súkromná stránka s reťazcami; súkromné ​​reťazcové tajomstvo; // štandardné getre a setre}

2.2. Zobrazuje sa widget

Na základe tutoriálu zo série teraz upravíme registrace.html zahrnúť knižnicu Google.

Do nášho registračného formulára pridáme widget reCAPTCHA, ktorý očakáva tento atribút data-sitekey obsahovať site-key.

Widget sa pripojí parameter požiadavky g-recaptcha-odpoveď keď boli predložené:

   ...    ...  ... 

3. Validácia na strane servera

Nový parameter požiadavky kóduje náš kľúč stránky a jedinečný reťazec identifikujúci úspešné dokončenie výzvy používateľom.

Pretože to však nedokážeme rozpoznať, nemôžeme dôverovať tomu, čo používateľ predložil, je legitímne. Na overenie servera sa podáva požiadavka na strane servera captcha odpoveď s API webových služieb.

Koncový bod prijíma požiadavku HTTP na adrese URL //www.google.com/recaptcha/api/siteverify s parametrami dopytu tajomstvo, odpoveďa remoteip. Vráti odpoveď JSON so schémou:

false, "challenge_ts": časová značka, "názov hostiteľa": reťazec, "chybové kódy": [...] 

3.1. Načítať odpoveď používateľa

Odozva užívateľa na výzvu reCAPTCHA sa získa z parametra požiadavky g-recaptcha-odpoveď použitím HttpServletRequest a overené pomocou nášho CaptchaService. Akákoľvek výnimka vyvolaná počas spracovania odpovede zruší zvyšok logiky registrácie:

verejná trieda RegistrationController {@Autowired private ICaptchaService captchaService; ... @RequestMapping (value = "/ user / registration", method = RequestMethod.POST) @ResponseBody public GenericResponse registerUserAccount (@Valid UserDto accountDto, požiadavka HttpServletRequest) {String response = request.getParameter ("g-recaptcha-response") ); captchaService.processResponse (odpoveď); // Zvyšok implementácie} ...}

3.2. Validačná služba

Získaná odpoveď captcha by mala byť najskôr dezinfikovaná. Používa sa jednoduchý regulárny výraz.

Ak odpoveď vyzerá legitímne, požiadame webovú službu s tajný kľúč, captcha odpoveďa klienta IP adresa:

verejná trieda CaptchaService implementuje ICaptchaService {@Autowired private CaptchaSettings captchaSettings; @Autowired private RestOperations restTemplate; private static Pattern RESPONSE_PATTERN = Pattern.compile ("[A-Za-z0-9 _-] +"); @Override public void processResponse (reťazcová odpoveď) {if (! ResponseSanityCheck (odpoveď)) {hodiť novú InvalidReCaptchaException ("odpoveď obsahuje neplatné znaky"); } URI verifyUri = URI.create (String.format ("//www.google.com/recaptcha/api/siteverify?secret=%s&response=%s&remoteip=%s", getReCaptchaSecret (), odpoveď, getClientIP ())) ; GoogleResponse googleResponse = restTemplate.getForObject (verifyUri, GoogleResponse.class); if (! googleResponse.isSuccess ()) {throw new ReCaptchaInvalidException ("reCaptcha nebol úspešne overený"); }} private boolean responseSanityCheck (reťazcová odpoveď) {návrat StringUtils.hasLength (odpoveď) && RESPONSE_PATTERN.matcher (odpoveď) .matches (); }}

3.3. Namietanie platnosti

Fazuľa Java zdobená Jackson anotácie zapuzdrujú odpoveď na overenie:

@JsonInclude (JsonInclude.Include.NON_NULL) @JsonIgnoreProperties (ignoreUnknown = true) @JsonPropertyOrder ({"success", "challenge_ts", "hostname", "error-codes"}) verejná trieda GoogleResponse {@JsonProperty ("success") súkromný booleovský úspech; @JsonProperty ("challenge_ts") súkromné ​​reťazce challengeTs; @JsonProperty ("názov hostiteľa") súkromný reťazec názov hostiteľa; @JsonProperty („chybové kódy“) súkromné ​​chybové kódy [] chybové kódy; @JsonIgnore public boolean hasClientError () {ErrorCode [] errors = getErrorCodes (); if (errors == null) {return false; } pre (ErrorCode error: errors) {switch (error) {case InvalidResponse: case MissingResponse: return true; }} return false; } static enum ErrorCode {MissingSecret, InvalidSecret, MissingResponse, InvalidResponse; private static Map errorsMap = nový HashMap (4); static {errorsMap.put ("missing-input-secret", MissingSecret); errorsMap.put ("invalid-input-secret", InvalidSecret); errorsMap.put ("missing-input-response", MissingResponse); errorsMap.put ("invalid-input-response", InvalidResponse); } @JsonCreator public static ErrorCode forValue (String value) {return errorsMap.get (value.toLowerCase ()); }} // štandardní zakladatelia a zakladatelia}

Ako je naznačené, hodnota pravdy v úspech property znamená, že používateľ bol overený. Inak chybové kódy vlastníctvo sa vyplní dôvodom.

The meno hosťa odkazuje na server, ktorý používateľa presmeroval na reCAPTCHA. Ak spravujete viac domén a chcete, aby všetky zdieľali rovnaký pár kľúčov, môžete si overiť meno hosťa majetok sám.

3.4. Zlyhanie overenia

V prípade zlyhania overenia sa vyvolá výnimka. Knižnica reCAPTCHA musí dať klientovi pokyn, aby vytvoril novú výzvu.

Robíme tak v obslužnej rutine chyby registrácie klienta vyvolaním resetu v knižnici grecaptcha miniaplikácia:

register (event) {event.preventDefault (); var formData = $ ('form'). serialize (); $ .post (serverContext + "používateľ / registrácia", formData, funkcia (dáta) {if (data.message == "success") {// úspech handler}}) .fail (funkcia (dáta) {grecaptcha.reset ( ); ... if (data.responseJSON.error == "InvalidReCaptcha") {$ ("# captchaError"). show (). html (data.responseJSON.message);} ...}}

4. Ochrana zdrojov servera

Škodliví klienti sa nemusia riadiť pravidlami karantény karantény. Naše myslenie v oblasti bezpečnosti by teda malo byť v zdrojoch, ktoré sú vystavené, a v tom, ako môžu byť zneužité.

4.1. Pokusy o medzipamäť

Je dôležité si uvedomiť, že integráciou reCAPTCHA bude každá zadaná požiadavka spôsobovať, že server vytvorí soket na overenie požiadavky.

Aj keď by sme potrebovali viacvrstvový prístup na skutočné zmiernenie DoS, môžeme implementovať elementárnu vyrovnávaciu pamäť, ktorá obmedzuje klienta na 4 neúspešné odpovede captcha:

verejná trieda ReCaptchaAttemptService {private int MAX_ATTEMPT = 4; private LoadingCache pokusyCache; public ReCaptchaAttemptService () {super (); pokusyCache = CacheBuilder.newBuilder () .expireAfterWrite (4, TimeUnit.HOURS) .build (nový CacheLoader () {@Override verejné celočíselné načítanie (reťazcový kľúč) {návrat 0;}}); } public void reCaptchaSucceeded (reťazcový kľúč) {pokusyCache.invalidate (kľúč); } public void reCaptchaFailed (reťazcový kľúč) {int pokusy = pokusyCache.getUnchecked (kľúč); pokusy ++; pokusyCache.put (kľúč, pokusy); } public boolean isBlocked (reťazcový kľúč) {návrat pokusyCache.getUnchecked (kľúč)> = MAX_ATTEMPT; }}

4.2. Refaktorovanie overovacej služby

Vyrovnávacia pamäť sa začlení najskôr prerušením, ak klient prekročil limit pokusu. Inak pri spracovaní neúspešného GoogleResponse zaznamenávame pokusy obsahujúce chybu s odpoveďou klienta. Úspešné overenie vymaže vyrovnávaciu pamäť pokusov:

verejná trieda CaptchaService implementuje ICaptchaService {@Autowired private ReCaptchaAttemptService reCaptchaAttemptService; ... @Override public void processResponse (reťazcová odpoveď) {... if (reCaptchaAttemptService.isBlocked (getClientIP ())) {throw new InvalidReCaptchaException ("Klient prekročil maximálny počet neúspešných pokusov"); } ... GoogleResponse googleResponse = ... if (! GoogleResponse.isSuccess ()) {if (googleResponse.hasClientError ()) {reCaptchaAttemptService.reCaptchaFailed (getClientIP ()); } hodiť novú ReCaptchaInvalidException ("reCaptcha nebola úspešne overená"); } reCaptchaAttemptService.reCaptchaSucceeded (getClientIP ()); }}

5. Integrácia reCAPTCHA v3 spoločnosti Google

ReCAPTCHA v3 od spoločnosti Google sa líši od predchádzajúcich verzií, pretože nevyžaduje žiadnu interakciu používateľa. Jednoducho dáva skóre za každú žiadosť, ktorú pošleme, a umožňuje nám rozhodnúť sa, aké konečné kroky treba pre našu webovú aplikáciu podniknúť.

Znova, aby sme integrovali reCAPTCHA 3 spoločnosti Google, musíme najskôr zaregistrovať náš web u tejto služby, pridať ich knižnicu na našu stránku a potom overiť odpoveď tokenu pomocou webovej služby.

Zaregistrujme teda našu stránku na //www.google.com/recaptcha/admin/create a po výbere reCAPTCHA v3 získame nové tajné a miestne kľúče.

5.1. Aktualizuje sa application.properties a Nastavenia Captcha

Po registrácii je potrebné vykonať aktualizáciu application.properties s novými kľúčmi a nami zvolenou prahovou hodnotou skóre:

google.recaptcha.key.site = 6LefKOAUAAAAAE ... google.recaptcha.key.secret = 6LefKOAUAAAA ... google.recaptcha.key.threshold = 0,5

Je dôležité si uvedomiť, že hranica je nastavená na 0.5 je predvolená hodnota a dá sa v priebehu času vyladiť analýzou skutočných prahových hodnôt v konzole správcu Google.

Ďalej aktualizujme naše Nastavenia Captcha trieda:

@Component @ConfigurationProperties (prefix = "google.recaptcha.key") verejná trieda CaptchaSettings {// ... ďalšie vlastnosti súkromný plavákový limit; // štandardné getre a setre}

5.2. Front-end integrácia

Teraz upravíme registrace.html zahrnúť knižnicu Google do kľúča našej stránky.

Do nášho registračného formulára pridáme skryté pole, ktoré uloží token odpovede prijatý z hovoru do grecaptcha.execute funkcia:

   ... ... ... ... ... ... var siteKey = /*[[${@captchaService.getReCaptchaSite()}]]*/; grecaptcha.execute (siteKey, {action: /*[[${T(com.baeldung.captcha.CaptchaService).REGISTER_ACTION}]]*/}).then(function(response) {$ ('# response'). val (odpoveď); var formData = $ ('form'). serialize ();

5.3. Validácia na strane servera

Na overenie tokenu odpovede pomocou rozhrania API webovej služby budeme musieť urobiť tú istú požiadavku na strane servera, aká sa nachádza v overení na strane servera reCAPTCHA.

Objekt JSON odpovede bude obsahovať dve ďalšie vlastnosti:

{... "skóre": číslo, "akcia": reťazec}

Skóre je založené na interakciách používateľa a je hodnotou medzi 0 (veľmi pravdepodobne robot) a 1,0 (veľmi pravdepodobne človek).

Action je nový koncept, ktorý spoločnosť Google predstavila, aby sme mohli na jednej webovej stránke vykonať veľa požiadaviek reCAPTCHA.

Pri každom spustení reCAPTCHA v3 musí byť zadaná akcia. A musíme overiť, či je hodnota akcia vlastnosť v odpovedi zodpovedá očakávanému názvu.

5.4. Získajte token odpovede

Token odpovede reCAPTCHA v3 sa získa z odpoveď použitie parametra požiadavky HttpServletRequest a overené pomocou nášho CaptchaService. Mechanizmus je identický s mechanizmom videným vyššie v reCAPTCHA:

verejná trieda RegistrationController {@Autowired private ICaptchaService captchaService; ... @RequestMapping (value = "/ user / registration", method = RequestMethod.POST) @ResponseBody public GenericResponse registerUserAccount (@Valid UserDto accountDto, požiadavka HttpServletRequest) {String response = request.getParameter ("response"); captchaService.processResponse (odpoveď, CaptchaService.REGISTER_ACTION); // zvyšok implementácie} ...}

5.5. Refaktorovanie overovacej služby s v3

Refactored CaptchaService trieda overovacej služby obsahuje a processResponse metóda analogická k processResponse metóda predchádzajúcej verzie, ale je potrebné skontrolovať akcia a skóre parametre GoogleResponse:

verejná trieda CaptchaService implementuje ICaptchaService {public static final String REGISTER_ACTION = "register"; ... @Override public void processResponse (odpoveď reťazca, akcia reťazca) {... GoogleResponse googleResponse = restTemplate.getForObject (verifyUri, GoogleResponse.class); if (! googleResponse.isSuccess () ||! googleResponse.getAction (). equals (action) || googleResponse.getScore () <captchaSettings.getThreshold ()) {... hod nový ReCaptchaInvalidException ("reCaptcha nebol úspešne overený") ); } reCaptchaAttemptService.reCaptchaSucceeded (getClientIP ()); }}

V prípade, že overenie zlyhá, urobíme výnimku, ale všimnite si, že v3 nie je resetovať metóda na vyvolanie v klientovi JavaScript.

Na ochranu serverových zdrojov budeme mať stále rovnakú implementáciu, ako je uvedené vyššie.

5.6. Aktualizuje sa GoogleResponse Trieda

Musíme pridať nové vlastnosti skóre a akcia do GoogleResponse Java bean:

@JsonPropertyOrder ({"úspech", "skóre", "akcia", "challenge_ts", "názov hostiteľa", "chybové kódy"}) verejná trieda GoogleResponse {// ... ďalšie vlastnosti @JsonProperty ("skóre") súkromné plavákové skóre; @JsonProperty ("akcia") súkromná reťazcová akcia; // štandardné getre a setre}

6. Záver

V tomto článku sme integrovali knižnicu reCAPTCHA spoločnosti Google do našej registračnej stránky a implementovali sme službu na overenie odpovede captcha s požiadavkou na strane servera.

Neskôr sme aktualizovali registračnú stránku pomocou knižnice reCAPTCHA v3 spoločnosti Google a zistili sme, že registračný formulár je štíhlejší, pretože používateľ už nemusí podniknúť žiadne kroky.

Plná implementácia tohto tutoriálu je k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found