Registracija s Springom - integrirajte reCAPTCHA

1. Pregled

U ovom uputstvu nastavit ćemo seriju Proljetna sigurnosna registracija dodavanjem GooglereCAPTCHA u postupak registracije kako bi se ljudi razlikovali od botova.

2. Integriranje Googleova reCAPTCHA

Da bismo integrirali Googleovu reCAPTCHA web-uslugu, prvo moramo registrirati našu stranicu s uslugom, dodati njihovu knjižnicu na našu stranicu, a zatim provjeriti captcha odgovor korisnika s web-uslugom.

Registrirajmo svoje web mjesto na //www.google.com/recaptcha/admin. Proces registracije generira a web-ključ i tajni ključ za pristup web-usluzi.

2.1. Pohranjivanje API-ključnog para

Ključeve pohranjujemo u svojstva.svojstva:

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

I izložite ih Proljeću koristeći grah označen s @ConfigurationProperties:

@Component @ConfigurationProperties (prefix = "google.recaptcha.key") javna klasa CaptchaSettings {privatni niz stranica; privatna tajna niza; // standardni geteri i postavljači}

2.2. Prikazivanje widgeta

Nadovezujući se na tutorial iz serije, sada ćemo izmijeniti registracija.html uključiti Googleovu knjižnicu.

Unutar našeg obrasca za registraciju dodajemo widget reCAPTCHA koji očekuje atribut data-sitekey sadržavati web-ključ.

Widget će se dodati parametar zahtjeva g-recaptcha-odgovor kad se preda:

   ...    ...  ... 

3. Provjera valjanosti na poslužitelju

Novi parametar zahtjeva kodira naš ključ web lokacije i jedinstveni niz koji identificira korisnikov uspješan završetak izazova.

Međutim, budući da sami to ne možemo prepoznati, ne možemo vjerovati da je ono što je korisnik podnio legitimno. Zahtjev na strani poslužitelja daje se za provjeru valjanosti datoteke captcha odgovor s API-jem web-usluga.

Krajnja točka prihvaća HTTP zahtjev na URL-u //www.google.com/recaptcha/api/siteverify, s parametrima upita tajna, odgovor, i remoteip. Vraća json odgovor koji ima shemu:

netačno, "challenge_ts": vremenska oznaka, "ime hosta": niz, "kodovi pogrešaka": [...] 

3.1. Dohvatite odgovor korisnika

Odgovor korisnika na izazov reCAPTCHA preuzima se iz parametra zahtjeva g-recaptcha-odgovor koristeći HttpServletRequest i potvrđeno s našim CaptchaService. Svaka iznimka izbačena tijekom obrade odgovora poništit će ostatak logike registracije:

javna klasa RegistrationController {@Autowired private ICaptchaService captchaService; ... @RequestMapping (value = "/ user / registration", method = RequestMethod.POST) @ResponseBody public GenericResponse registerUserAccount (@Valid UserDto accountDto, HttpServletRequest request) {String response = request.getParameter ("g-recaptcha-response" ); captchaService.processResponse (odgovor); // Ostatak provedbe} ...}

3.2. Usluga provjere valjanosti

Dobiveni captcha odgovor prvo treba sanirati. Koristi se jednostavan regularni izraz.

Ako odgovor izgleda legitimno, tada web stranici šaljemo zahtjev s tajni ključ, captcha odgovor, i klijenta IP adresa:

javna klasa CaptchaService implementira ICaptchaService {@Autowired private CaptchaSettings captchaSettings; @Autowired private RestOperations restTemplate; privatni statički obrazac RESPONSE_PATTERN = Pattern.compile ("[A-Za-z0-9 _-] +"); @Override public void processResponse (String response) {if (! ResponseSanityCheck (response)) {throw new InvalidReCaptchaException ("Odgovor sadrži nevaljane znakove"); } URI verifyUri = URI.create (String.format ("//www.google.com/recaptcha/api/siteverify?secret=%s&response=%s&remoteip=%s", getReCaptchaSecret (), response, getClientIP ())) ; GoogleResponse googleResponse = restTemplate.getForObject (verifyUri, GoogleResponse.class); if (! googleResponse.isSuccess ()) {throw new ReCaptchaInvalidException ("reCaptcha nije uspješno provjerena"); }} privatni logički responseSanityCheck (odgovor niza) {return StringUtils.hasLength (odgovor) && RESPONSE_PATTERN.matcher (odgovor) .matches (); }}

3.3. Objektivizacija provjere valjanosti

Java-grah ukrašen Jackson bilješke enkapsuliraju odgovor provjere valjanosti:

@JsonInclude (JsonInclude.Include.NON_NULL) @JsonIgnoreProperties (ignoreUnknown = true) @JsonPropertyOrder ({"success", "challenge_ts", "hostname", "error-kodovi"}) Google Class Response {@JsonProperty ("uspjeh") privatni booleov uspjeh; @JsonProperty ("challenge_ts") private String challengeTs; @JsonProperty ("hostname") private String ime hosta; @JsonProperty ("kodovi pogrešaka") private ErrorCode [] errorCodes; @JsonIgnore public boolean hasClientError () {ErrorCode [] pogreške = getErrorCodes (); if (pogreške == null) {return false; } za (ErrorCode error: error) {switch (error) {case InvalidResponse: case MissingResponse: return true; }} return false; } static enum ErrorCode {MissingSecret, InvalidSecret, MissingResponse, InvalidResponse; privatna statička karta errorMap = novi HashMap (4); statička {errorMap.put ("nedostaje-ulaz-tajna", MissingSecret); errorMap.put ("invalid-input-secret", InvalidSecret); errorMap.put ("nedostaje-ulaz-odgovor", MissingResponse); errorMap.put ("invalid-input-response", InvalidResponse); } @JsonCreator javna statička šifra pogreške za vrijednost (vrijednost niza) {return errorMap.get (value.toLowerCase ()); }} // standardni getteri i postavljači}

Kao što se podrazumijeva, vrijednost istine u uspjeh svojstvo znači da je korisnik provjeren. Inače kodovi pogrešaka imovina će se naseliti s razlogom.

The ime domaćina odnosi se na poslužitelj koji je korisnika preusmjerio na reCAPTCHA. Ako upravljate mnogim domenama i želite da sve dijele isti par ključeva, možete odabrati potvrdu ime domaćina imanje sebe.

3.4. Neuspjeh provjere valjanosti

U slučaju neuspjeha provjere valjanosti, izbacuje se iznimka. Biblioteka reCAPTCHA treba uputiti klijenta da stvori novi izazov.

To radimo u upravljaču pogreškama pri registraciji klijenta, pozivajući se na reset na knjižnici grecaptcha widget:

register (event) {event.preventDefault (); var formData = $ ('obrazac'). serialize (); $ .post (serverContext + "korisnik / registracija", formData, funkcija (podaci) {if (data.message == "uspjeh") {// rukovatelj uspjehom}}) .fail (funkcija (podaci) {grecaptcha.reset ( ); ... if (data.responseJSON.error == "InvalidReCaptcha") {$ ("# captchaError"). show (). html (data.responseJSON.message);} ...}}

4. Zaštita resursa poslužitelja

Zlonamjerni klijenti ne trebaju se pridržavati pravila pješčanika u pregledniku. Dakle, naš način razmišljanja o sigurnosti trebao bi biti na izloženim resursima i na koji bi se način mogao zlorabiti.

4.1. Predmemorija pokušaja

Važno je razumjeti da će integriranjem reCAPTCHA svaki upućeni zahtjev uzrokovati da poslužitelj stvori utičnicu za provjeru valjanosti zahtjeva.

Iako bi nam trebao slojevitiji pristup za istinsko ublažavanje DoS-a, možemo implementirati elementarnu predmemoriju koja klijenta ograničava na 4 neuspjela captcha odgovora:

javna klasa ReCaptchaAttemptService {private int MAX_ATTEMPT = 4; privatni LoadingCache pokušajiCache; javna ReCaptchaAttemptService () {super (); poskusiCache = CacheBuilder.newBuilder () .expireAfterWrite (4, TimeUnit.HOURS) .build (new CacheLoader () {@ Nadjačaj javno integrirano učitavanje (ključ niza) {return 0;}}); } javna praznina reCaptchaSucceeded (ključ niza) {pokušajiCache.invalidate (ključ); } javna void reCaptchaFailed (ključ niza) {int pokušaji = pokušajiCache.getUn Check (ključ); pokušava ++; testsCache.put (ključ, pokušaji); } javna logička vrijednost isBlocked (ključ niza) {return pokušajiCache.getUn провеreno (tipka)> = MAX_ATTEMPT; }}

4.2. Refaktoriranje usluge provjere valjanosti

Predmemorija se prvo ugrađuje prekida ako je klijent premašio ograničenje pokušaja. Inače kada je obrada neuspješna GoogleResponse bilježimo pokušaje koji sadrže pogrešku s odgovorom klijenta. Uspješnom provjerom valjanosti briše se predmemorija pokušaja:

javna klasa CaptchaService implementira ICaptchaService {@Autowired private ReCaptchaAttemptService reCaptchaAttemptService; ... @Override public void processResponse (String response) {... if (reCaptchaAttemptService.isBlocked (getClientIP ())) {throw new InvalidReCaptchaException ("Klijent premašio maksimalan broj neuspjelih pokušaja"); } ... GoogleResponse googleResponse = ... if (! GoogleResponse.isSuccess ()) {if (googleResponse.hasClientError ()) {reCaptchaAttemptService.reCaptchaFailed (getClientIP ()); } baciti novi ReCaptchaInvalidException ("reCaptcha nije uspješno provjerena"); } reCaptchaAttemptService.reCaptchaSucceeded (getClientIP ()); }}

5. Integriranje Googleova reCAPTCHA v3

Googleov reCAPTCHA v3 razlikuje se od prethodnih verzija jer ne zahtijeva korisničku interakciju. Jednostavno daje ocjenu za svaki zahtjev koji pošaljemo i omogućuje nam da odlučimo koje ćemo posljednje radnje poduzeti za našu web aplikaciju.

Opet, da bismo integrirali Googleovu reCAPTCHA 3, prvo moramo registrirati našu stranicu s uslugom, dodati njihovu biblioteku na našu stranicu, a zatim provjeriti token odgovor s web uslugom.

Dakle, registrirajmo našu stranicu na //www.google.com/recaptcha/admin/create i, nakon odabira reCAPTCHA v3, dobit ćemo novu tajnu i ključeve web mjesta.

5.1. Ažuriranje primjena.svojstva i Postavke Captcha

Nakon registracije, trebamo ažurirati primjena.svojstva s novim ključevima i odabranom vrijednošću praga rezultata:

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

Važno je napomenuti da je prag postavljen na 0.5 zadana je vrijednost i može se podesiti s vremenom analizom stvarnih vrijednosti praga na Googleovoj administratorskoj konzoli.

Dalje, ažurirajmo naš Postavke Captcha razred:

@Component @ConfigurationProperties (prefix = "google.recaptcha.key") javna klasa CaptchaSettings {// ... ostala svojstva private float prag; // standardni geteri i postavljači}

5.2. Front-End integracija

Sada ćemo izmijeniti registracija.html uključiti Googleovu knjižnicu s ključem naše web lokacije.

Unutar našeg registracijskog obrasca dodajemo skriveno polje u koje će se pohraniti token odgovora primljen od poziva na grecaptcha.izvršiti funkcija:

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

5.3. Provjera na strani poslužitelja

Morat ćemo uputiti isti zahtjev na strani poslužitelja koji se vidi u reCAPTCHA provjeri na strani poslužitelja da bismo provjerili valjanost odgovora API-jem web usluge.

Odgovor JSON objekt sadržavat će dva dodatna svojstva:

{... "score": broj, "action": string}

Rezultat se temelji na interakciji korisnika i vrijednost je između 0 (vrlo vjerojatno bot) i 1,0 (vrlo vjerojatno čovjek).

Action je novi koncept koji je Google uveo kako bismo mogli izvršavati mnoge reCAPTCHA zahtjeve na istoj web stranici.

Akcija se mora navesti svaki put kada izvršimo reCAPTCHA v3. I, moramo provjeriti je li vrijednost akcijski svojstvo u odgovoru odgovara očekivanom imenu.

5.4. Dohvatite žeton odgovora

Token odgovora reCAPTCHA v3 preuzima se iz odgovor parametar zahtjeva pomoću HttpServletRequest i potvrđeno s našim CaptchaService. Mehanizam je identičan onome viđenom gore u reCAPTCHA:

javna klasa RegistrationController {@Autowired private ICaptchaService captchaService; ... @RequestMapping (value = "/ user / registration", method = RequestMethod.POST) @ResponseBody public GenericResponse registerUserAccount (@Valid UserDto accountDto, HttpServletRequest request) {String response = request.getParameter ("response"); captchaService.processResponse (odgovor, CaptchaService.REGISTER_ACTION); // ostatak implementacije} ...}

5.5. Refaktoriranje usluge provjere valjanosti pomoću v3

Prepravljeno CaptchaService klasa usluge provjere valjanosti sadrži a processResponse metoda analogna processResponse metoda prethodne verzije, ali pazi da provjeri akcijski i postići parametri GoogleResponse:

javna klasa CaptchaService implementira ICaptchaService {javni statički konačni String REGISTER_ACTION = "register"; ... @Override public void processResponse (String response, String action) {... GoogleResponse googleResponse = restTemplate.getForObject (verifyUri, GoogleResponse.class); if (! googleResponse.isSuccess () ||! googleResponse.getAction (). equals (action) || googleResponse.getScore () <captchaSettings.getThreshold ()) {... baciti novi ReCaptchaInvalidException ("reCaptcha nije uspješno potvrđena" ); } reCaptchaAttemptService.reCaptchaSucceeded (getClientIP ()); }}

U slučaju da provjera valjanosti ne uspije, izbacit ćemo iznimku, ali imajte na umu da s v3 nema resetirati metodu za pozivanje u JavaScript klijentu.

I dalje ćemo imati istu implementaciju viđenu gore za zaštitu resursa poslužitelja.

5.6. Ažuriranje GoogleResponse Razred

Moramo dodati nova svojstva postići i akcijski prema GoogleResponse Java grah:

@JsonPropertyOrder ({"uspjeh", "rezultat", "radnja", "izazov_t", "ime hosta", "kodovi pogrešaka"}) javna klasa GoogleResponse {// ... ostala svojstva @JsonProperty ("rezultat") privatno rezultat s plutanjem; @JsonProperty ("action") private String akcija; // standardni geteri i postavljači}

6. Zaključak

U ovom smo članku integrirali Googleovu knjižnicu reCAPTCHA u našu stranicu za registraciju i implementirali uslugu za provjeru captcha odgovora sa zahtjevom na strani poslužitelja.

Kasnije smo stranicu za registraciju nadogradili Googleovom bibliotekom reCAPTCHA v3 i vidjeli da obrazac za registraciju postaje vitkiji jer korisnik više ne mora poduzimati nikakve radnje.

Potpuna implementacija ovog vodiča dostupna je na GitHubu.


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