Regisztráció a tavasszal - integrálja a reCAPTCHA-t

1. Áttekintés

Ebben az oktatóanyagban folytatjuk a tavaszi biztonsági regisztráció sorozatot hozzáadásával GooglereCAPTCHA a regisztrációs folyamathoz annak érdekében, hogy megkülönböztessék az embert a robotoktól.

2. A Google reCAPTCHA integrálása

A Google reCAPTCHA webszolgáltatásának integrálásához először regisztrálnunk kell webhelyünket a szolgáltatással, hozzá kell adnunk könyvtárukat az oldalunkhoz, majd ellenőriznünk kell a felhasználó captcha válaszát a webszolgáltatással.

Regisztráljuk webhelyünket a //www.google.com/recaptcha/admin címen. A regisztrációs folyamat generálja a site-key és titkos kulcs a web-szolgáltatás eléréséhez.

2.1. Az API Key-Pair tárolása

A kulcsokat a application.properties:

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

És tedd ki őket Spring-nek egy babgal, amelyet a következőkkel jelölünk @ConfigurationProperties:

@Component @ConfigurationProperties (prefix = "google.recaptcha.key") public class CaptchaSettings {private String site; privát String titok; // szabványos mérőeszközök és beállítók}

2.2. A Widget megjelenítése

A sorozatból származó oktatóanyagra építve most módosítjuk a regisztráció.html hogy felvegye a Google könyvtárát.

A regisztrációs űrlapunkon belül hozzáadjuk a reCAPTCHA modult, amely elvárja az attribútumot data-sitekey hogy tartalmazzák a site-key.

A widget hozzáfűződik a kérés paraméter g-recaptcha-válasz amikor benyújtják:

   ...    ...  ... 

3. Kiszolgálóoldali ellenőrzés

Az új kérés paraméter kódolja a webhelykulcsunkat és egy egyedi karakterláncot, amely azonosítja a felhasználó sikeres teljesítését.

Mivel azonban ezt mi magunk sem tudjuk felismerni, nem bízhatunk abban, hogy a felhasználó által benyújtott adatok jogosak-e. Kiszolgálóoldali kérés érkezik a captcha válasz a webszolgáltatás API-val.

A végpont elfogadja a HTTP-kérést az URL-en //www.google.com/recaptcha/api/siteverify a lekérdezési paraméterekkel titok, válasz, és távirányító. Visszaadja a sémával rendelkező json választ:

hamis, "challenge_ts": időbélyeg, "hostname": karakterlánc, "hibakódok": [...] 

3.1. A felhasználó válaszának beolvasása

A felhasználó válaszát a reCAPTCHA kihívásra a kérési paraméterből kapjuk meg g-recaptcha-válasz felhasználásával HttpServletRequest és érvényesítette a mi CaptchaService. A válasz feldolgozása során felmerülő kivétel megszakítja a regisztrációs logika többi részét:

public class 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 (válasz); // A megvalósítás többi része} ...}

3.2. Érvényesítési szolgáltatás

A kapott captcha választ először fertőtleníteni kell. Egyszerű szabályos kifejezést használunk.

Ha a válasz jogosnak tűnik, akkor kérést intézünk a webszolgáltatáshoz a titkos kulcs, a captcha válaszés az ügyfélét IP-cím:

nyilvános osztályú CaptchaService megvalósítja az ICaptchaService {@Autowired private CaptchaSettings captchaSettings; @Autowired private RestOperations restTemplate; privát statikus minta RESPONSE_PATTERN = Pattern.compile ("[A-Za-z0-9 _-] +"); @Orride public void processResponse (String response) {if (! ResponseSanityCheck (response)) {dobjon új InvalidReCaptchaException-t ("A válasz érvénytelen karaktereket tartalmaz"); } URI verUU = URI.create (String.format ("//www.google.com/recaptcha/api/siteverify?secret=%s&response=%s&remoteip=%s", getReCaptchaSecret (), válasz, getClientIP ())) ; GoogleResponse googleResponse = restTemplate.getForObject (verUri, GoogleResponse.class); if (! googleResponse.isSuccess ()) {dobjon új ReCaptchaInvalidException-t ("a reCaptchát nem sikerült érvényesíteni"); }} privát logikai válaszSanityCheck (String response) {return StringUtils.hasLength (response) && RESPONSE_PATTERN.matcher (response) .matches (); }}

3.3. Az érvényesítés objektiválása

Javával díszített Java-bab Jackson az annotációk összefoglalják az érvényesítési választ:

@JsonInclude (JsonInclude.Include.NON_NULL) @JsonIgnoreProperties (ignoreUnknown = true) @JsonPropertyOrder ({"siker", "challenge_ts", "gazdagépnév", "hibakódok"}) nyilvános osztály GoogleResponse {@JsonProperty ("siker") privát logikai siker; @JsonProperty ("challenge_ts") privát String challengeTs; @JsonProperty ("hostname") privát karakterlánc hostname; @JsonProperty ("hibakódok") privát ErrorCode [] hibakódok; @JsonIgnore nyilvános logikai hasClientError () {ErrorCode [] hibák = getErrorCodes (); if (hibák == null) {return false; } for (ErrorCode error: hibák) {switch (error) {case InvalidResponse: case MissingResponse: return true; }} return false; } static enum ErrorCode {MissingSecret, InvalidSecret, MissingResponse, InvalidResponse; privát statikus térkép hibákMap = new HashMap (4); statikus {hibákMap.put ("hiányzó bemenet-titok", MissingSecret); errorsMap.put ("érvénytelen-input-titok", InvalidSecret); errorsMap.put ("missing-input-response", MissingResponse); errorsMap.put ("érvénytelen-input-válasz", InvalidResponse); } @JsonCreator public static ErrorCode forValue (String value) {return errorsMap.get (value.toLowerCase ()); }} // szabványos mérőeszközök és beállítók}

Mint sejtettük, a siker tulajdonság azt jelenti, hogy a felhasználót validálták. Egyébként a errorCodes vagyon feltölti az okot.

A hostname arra a kiszolgálóra utal, amely átirányította a felhasználót a reCAPTCHA-ra. Ha sok domaint kezel, és azt szeretné, ha mindegyik ugyanazt a kulcspárt osztaná meg, akkor dönthet úgy, hogy igazolja a domaint hostname birtokolja magát.

3.4. Érvényesítési hiba

Validációs hiba esetén kivételt dob. A reCAPTCHA könyvtárnak utasítania kell az ügyfelet egy új kihívás létrehozására.

Ezt az ügyfél regisztrációs hibakezelőjében tesszük, a könyvtár visszaállításának meghívásával grecaptcha widget:

regisztráció (esemény) {event.preventDefault (); var formData = $ ('forma'). serialize (); $ .post (serverContext + "felhasználó / regisztráció", formData, függvény (adatok) {if (data.message == "siker") {// sikerkezelő}}) .fail (függvény (adat) {grecaptcha.reset ( ); ... if (data.responseJSON.error == "InvalidReCaptcha") {$ ("# captchaError"). show (). html (data.responseJSON.message);} ...}}

4. A kiszolgáló erőforrásainak védelme

A rosszindulatú klienseknek nem kell betartaniuk a böngésző homokozójának szabályait. Tehát a biztonsági gondolkodásmódunknak a kitett erőforrásoknál kell lennie, és hogyan lehet visszaélni velük.

4.1. Megpróbálja a gyorsítótárat

Fontos megérteni, hogy a reCAPTCHA integrálásával minden megtett kérés arra készteti a kiszolgálót, hogy létrehozzon egy foglalatot a kérés érvényesítésére.

Noha többszintű megközelítésre lenne szükségünk a valódi DoS mérsékléshez, megvalósíthatunk egy elemi gyorsítótárat, amely az ügyfelet 4 sikertelen captcha válaszra korlátozza:

public class ReCaptchaAttemptService {private int MAX_ATTEMPT = 4; privát LoadingCache kísérletekCache; public ReCaptchaAttemptService () {super (); kísérletekCache = CacheBuilder.newBuilder () .expireAfterWrite (4, TimeUnit.HOURS) .build (new CacheLoader () {@Override public Integer load (String key) {return 0;}}); } public void reCaptchaSucceeded (karakterlánc-kulcs) {kísérletekCache.invalidate (kulcs); } public void reCaptchaFailed (karakterlánc-kulcs) {int kísérletek = kísérletekCache.getUnchecked (kulcs); kísérletek ++; kísérletekCache.put (kulcs, kísérletek); } nyilvános logikai érték blokkolt (karakterlánc-kulcs) {return kísérletekCache.getUnchecked (kulcs)> = MAX_ATTEMPT; }}

4.2. Az érvényesítési szolgáltatás visszafogása

A gyorsítótár beépítésre kerül a megszakítással, ha az ügyfél túllépte a kísérleti korlátot. Ellenkező esetben sikertelen feldolgozásakor GoogleResponse rögzítjük a hibát tartalmazó kísérleteket az ügyfél válaszával. A sikeres érvényesítés törli a kísérletek gyorsítótárát:

a CaptchaService nyilvános osztály megvalósítja az ICaptchaService {@Autowired private ReCaptchaAttemptService reCaptchaAttemptService; ... @Orride public void processResponse (String response) {... if (reCaptchaAttemptService.isBlocked (getClientIP ())) {dob új InvalidReCaptchaException ("Az ügyfél túllépte a sikertelen próbálkozások maximális számát"); } ... GoogleResponse googleResponse = ... if (! GoogleResponse.isSuccess ()) {if (googleResponse.hasClientError ()) {reCaptchaAttemptService.reCaptchaFailed (getClientIP ()); } dobja az új ReCaptchaInvalidException parancsot ("a reCaptcha programot nem sikerült érvényesíteni"); } reCaptchaAttemptService.reCaptchaSucceeded (getClientIP ()); }}

5. A Google reCAPTCHA v3 integrálása

A Google reCAPTCHA v3 eltér az előző verzióktól, mert nem igényel semmilyen felhasználói beavatkozást. Egyszerűen pontot ad minden elküldött kéréshez, és lehetővé teszi, hogy eldöntsük, milyen végső lépéseket tegyünk webalkalmazásunkhoz.

A Google reCAPTCHA 3 integrálásához először is regisztrálnunk kell webhelyünket a szolgáltatással, hozzá kell adnunk a könyvtárukat az oldalunkhoz, majd ellenőriznünk kell a token választ a webszolgáltatással.

Tehát regisztráljuk webhelyünket a következő címen: //www.google.com/recaptcha/admin/create és a reCAPTCHA v3 kiválasztása után megszerezzük az új titkos és webhelykulcsokat.

5.1. Frissítés alkalmazás.tulajdonságok és CaptchaSettings

A regisztráció után frissítenünk kell alkalmazás.tulajdonságok az új gombokkal és a választott pontszám küszöbértékkel:

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

Fontos megjegyezni, hogy a küszöbérték beállítva 0.5 alapértelmezett érték, és idővel beállítható a valós küszöbértékek elemzésével a Google felügyeleti konzolban.

Ezután frissítsük a frissítéseket CaptchaSettings osztály:

@Component @ConfigurationProperties (prefix = "google.recaptcha.key") public class CaptchaSettings {// ... egyéb tulajdonságok private floatküszöb; // szabványos mérőeszközök és beállítók}

5.2. Front-end integráció

Most módosítjuk a regisztráció.html hogy beépítse a Google könyvtárát a webhelykulcsunkba.

A regisztrációs űrlapunkon belül egy rejtett mezőt adunk hozzá, amely a hívásból kapott válaszjelzőt tárolja a grecaptcha.execute funkció:

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

5.3. Kiszolgálóoldali ellenőrzés

Ugyanazt a kiszolgálóoldali kérést kell megadnunk, amelyet a reCAPTCHA kiszolgálóoldali érvényesítésben láthattunk, hogy a válasz tokent a webszolgáltatás API-val érvényesítsük.

A válasz JSON objektum két további tulajdonságot tartalmaz:

{... "score": szám, "action": string}

A pontszám a felhasználó interakcióin alapul, és értéke 0 (nagy valószínűséggel bot) és 1,0 (nagy valószínűséggel ember) között van.

Az Action egy új koncepció, amelyet a Google vezetett be, hogy sok reCAPTCHA kérést végrehajtsunk ugyanazon a weboldalon.

A reCAPTCHA v3 minden végrehajtásakor meg kell adni egy műveletet. És ellenőriznünk kell, hogy a akció tulajdonság a válaszban megegyezik a várható névvel.

5.4. Töltse le a Válaszjelzőt

A reCAPTCHA v3 válaszjelzőt a válasz kérjen paramétert HttpServletRequest és érvényesítette a mi CaptchaService. A mechanizmus megegyezik a reCAPTCHA fentiekben láthatóval:

public class 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 ("válasz"); captchaService.processResponse (válasz, CaptchaService.REGISTER_ACTION); // a megvalósítás többi része} ...}

5.5. Az érvényesítési szolgáltatás átalakítása a v3 verzióval

A visszafogott CaptchaService validációs szolgáltatási osztály tartalmazza a processResponse módszer analógja a processResponse módszer az előző verzióhoz, de ügyel arra, hogy ellenőrizze a akció és a pontszám paraméterei GoogleResponse:

public class A CaptchaService megvalósítja az ICaptchaService {public static final String REGISTER_ACTION = "regisztráció"; ... @Orride public void processResponse (String response, String action) {... GoogleResponse googleResponse = restTemplate.getForObject (verifyUri, GoogleResponse.class); if (! googleResponse.isSuccess () ||! googleResponse.getAction (). egyenlő (action) || googleResponse.getScore () <captchaSettings.getThreshold ()) {... dobja az új ReCaptchaInvalidException-t ("a reCaptcha programot nem sikerült érvényesíteni" ); } reCaptchaAttemptService.reCaptchaSucceeded (getClientIP ()); }}

Ha az érvényesítés nem sikerül, kivételt vetünk, de vegye figyelembe, hogy a v3 esetén nincs Visszaállítás metódus a JavaScript kliensben.

A szerver erőforrások védelme érdekében továbbra is ugyanaz a megvalósítás látható, mint fent.

5.6. A. Frissítése GoogleResponse Osztály

Hozzá kell adnunk az új tulajdonságokat pontszám és akció hoz GoogleResponse Java bab:

@JsonPropertyOrder ({"siker", "pontszám", "művelet", "challenge_ts", "gazdagépnév", "hibakódok"}) nyilvános osztály GoogleResponse {// ... egyéb tulajdonságok @JsonProperty ("pontszám") privát úszó pontszám; @JsonProperty ("action") privát String akció; // szabványos mérőeszközök és beállítók}

6. Következtetés

Ebben a cikkben beépítettük a Google reCAPTCHA könyvtárát regisztrációs oldalunkba, és megvalósítottunk egy szolgáltatást a captcha válasz kiszolgálóoldali kéréssel történő ellenőrzésére.

Később frissítettük a regisztrációs oldalt a Google reCAPTCHA v3 könyvtárával, és láttuk, hogy a regisztrációs űrlap karcsúbbá válik, mivel a felhasználónak már nem kell semmit sem tennie.

Az oktatóanyag teljes megvalósítása elérhető a GitHubon.