RecaptchaService
Descriere Generală
RecaptchaService
este serviciul responsabil pentru integrarea și executarea Google reCAPTCHA în aplicație. Serviciul gestionează logica de afișare a captcha-ului bazată pe starea de autentificare a utilizatorului și configurările aplicației, oferind protecție împotriva bot-urilor și spam-ului.
Configurare
Înregistrare în Dependency Injection
// În Program.cs sau Startup.cs
builder.Services.AddScoped<IRecaptchaService, RecaptchaService>();
// Asigurați-vă că dependențele sunt înregistrate
builder.Services.AddScoped<IConfigurationService, ConfigurationService>();
builder.Services.AddScoped<IUserService, UserService>();
Configurare reCAPTCHA
// În appsettings.json
{
"ReCaptcha": {
"SiteKey": "your-site-key-here",
"SecretKey": "your-secret-key-here",
"ValidateAuthenticated": false
}
}
Includere script Google reCAPTCHA
<!-- În index.html sau _Host.cshtml -->
<script src="https://www.google.com/recaptcha/api.js?render=explicit" async defer></script>
<script>
window.runCaptcha = (siteKey) => {
return new Promise((resolve, reject) => {
grecaptcha.ready(() => {
grecaptcha.execute(siteKey, { action: 'submit' })
.then(token => resolve(token))
.catch(error => reject(error));
});
});
};
</script>
Interfața IRecaptchaService
public interface IRecaptchaService
{
Task<string> Execute();
}
Metode disponibile
Execute()
Execută verificarea reCAPTCHA și returnează token-ul.
Parametri: Niciun parametru
Returnează: Task<string>
- Token-ul reCAPTCHA sau string gol dacă nu este necesar
Comportament: - Obține configurarea reCAPTCHA prin IConfigurationService - Verifică dacă utilizatorul este autentificat - Dacă utilizatorul nu este autentificat SAU ValidateAuthenticated este true, execută captcha - Dacă utilizatorul este autentificat și ValidateAuthenticated este false, returnează string gol - Apelează funcția JavaScript pentru a obține token-ul
Exemple de utilizare
În formular de contact
@inject IRecaptchaService RecaptchaService
<EditForm Model="@contactModel" OnValidSubmit="@SubmitForm">
<DataAnnotationsValidator />
<div class="form-group">
<label>Nume</label>
<InputText @bind-Value="contactModel.Name" class="form-control" />
<ValidationMessage For="@(() => contactModel.Name)" />
</div>
<div class="form-group">
<label>Email</label>
<InputText @bind-Value="contactModel.Email" class="form-control" />
<ValidationMessage For="@(() => contactModel.Email)" />
</div>
<div class="form-group">
<label>Mesaj</label>
<InputTextArea @bind-Value="contactModel.Message" class="form-control" />
<ValidationMessage For="@(() => contactModel.Message)" />
</div>
<FodButton Type="submit" Disabled="@isSubmitting">
@if (isSubmitting)
{
<FodLoadingCircular Size="FodSize.Small" />
}
else
{
<text>Trimite</text>
}
</FodButton>
</EditForm>
@code {
private ContactModel contactModel = new();
private bool isSubmitting;
private async Task SubmitForm()
{
isSubmitting = true;
try
{
// Execută reCAPTCHA
var captchaToken = await RecaptchaService.Execute();
// Trimite formularul cu token-ul
await SendContactForm(contactModel, captchaToken);
// Success
ShowSuccess("Mesaj trimis cu succes!");
contactModel = new();
}
catch (Exception ex)
{
ShowError("Eroare la trimiterea mesajului");
}
finally
{
isSubmitting = false;
}
}
}
În formular de înregistrare
@inject IRecaptchaService RecaptchaService
@inject IAuthService AuthService
<div class="registration-form">
<h3>Înregistrare cont nou</h3>
<EditForm Model="@registrationModel" OnValidSubmit="@Register">
<DataAnnotationsValidator />
<ValidationSummary />
<FodInput @bind-Value="registrationModel.Email"
Label="Email"
Type="email"
Required="true" />
<FodInput @bind-Value="registrationModel.Password"
Label="Parolă"
Type="password"
Required="true" />
<FodInput @bind-Value="registrationModel.ConfirmPassword"
Label="Confirmă parola"
Type="password"
Required="true" />
<FodButton Type="submit"
Color="FodColor.Primary"
FullWidth="true"
Disabled="@isRegistering">
Înregistrează-te
</FodButton>
</EditForm>
</div>
@code {
private RegistrationModel registrationModel = new();
private bool isRegistering;
private async Task Register()
{
isRegistering = true;
try
{
// Obține token reCAPTCHA
var recaptchaToken = await RecaptchaService.Execute();
if (string.IsNullOrEmpty(recaptchaToken))
{
// Pentru utilizatori autentificați când ValidateAuthenticated = false
// Continuă fără verificare
}
// Înregistrare cu token
var result = await AuthService.Register(registrationModel, recaptchaToken);
if (result.Succeeded)
{
NavigationManager.NavigateTo("/login");
}
}
catch (JSException)
{
ShowError("Eroare la verificarea reCAPTCHA. Reîncărcați pagina.");
}
finally
{
isRegistering = false;
}
}
}
Cu componenta FodRecaptcha
<FodRecaptcha @ref="recaptchaComponent" />
<EditForm Model="@model" OnValidSubmit="@HandleSubmit">
<!-- Form fields -->
<FodButton Type="submit">Trimite</FodButton>
</EditForm>
@code {
private FodRecaptcha recaptchaComponent;
private async Task HandleSubmit()
{
// FodRecaptcha folosește intern RecaptchaService
var token = await recaptchaComponent.GetTokenAsync();
if (!string.IsNullOrEmpty(token))
{
// Procesare cu token valid
}
}
}
Integrare cu backend
Validare token pe server
[HttpPost]
public async Task<IActionResult> SubmitForm([FromBody] FormModel model)
{
if (!string.IsNullOrEmpty(model.RecaptchaToken))
{
var isValid = await ValidateRecaptcha(model.RecaptchaToken);
if (!isValid)
{
return BadRequest("Verificare reCAPTCHA eșuată");
}
}
// Procesare formular
return Ok();
}
private async Task<bool> ValidateRecaptcha(string token)
{
var secretKey = Configuration["ReCaptcha:SecretKey"];
var client = new HttpClient();
var response = await client.PostAsync(
$"https://www.google.com/recaptcha/api/siteverify?secret={secretKey}&response={token}",
null);
var json = await response.Content.ReadAsStringAsync();
dynamic result = JsonSerializer.Deserialize<dynamic>(json);
return result.success == true && result.score > 0.5;
}
Tratare erori
Service cu gestionare erori
public class SafeRecaptchaService : IRecaptchaService
{
private readonly RecaptchaService _innerService;
private readonly ILogger<SafeRecaptchaService> _logger;
public async Task<string> Execute()
{
try
{
return await _innerService.Execute();
}
catch (JSException jsEx)
{
_logger.LogError(jsEx, "Eroare JavaScript la executarea reCAPTCHA");
throw new RecaptchaException("reCAPTCHA nu este disponibil", jsEx);
}
catch (TaskCanceledException)
{
_logger.LogWarning("Timeout la executarea reCAPTCHA");
return string.Empty; // Permite continuarea fără captcha
}
}
}
Fallback pentru erori
@code {
private async Task<string> GetRecaptchaTokenSafe()
{
try
{
return await RecaptchaService.Execute();
}
catch (Exception ex)
{
Logger.LogError(ex, "Eroare reCAPTCHA");
// Afișează captcha alternativ sau permite continuarea
var allowWithoutCaptcha = await ConfirmAsync(
"Verificarea reCAPTCHA a eșuat. Continuați fără verificare?");
return allowWithoutCaptcha ? "bypass" : throw;
}
}
}
Note tehnice
- Conditional execution - Execută doar pentru utilizatori neautentificați
- Configuration driven - Comportament controlat prin configurare
- JS dependency - Necesită funcția JavaScript
runCaptcha
- Async operation - Toate operațiile sunt asincrone
- Token expiration - Token-urile expiră după 2 minute
Configurări avansate
Pentru diferite acțiuni
window.runCaptcha = (siteKey, action = 'submit') => {
return new Promise((resolve, reject) => {
grecaptcha.ready(() => {
grecaptcha.execute(siteKey, { action: action })
.then(token => resolve(token))
.catch(error => reject(error));
});
});
};
Service extins
public interface IExtendedRecaptchaService : IRecaptchaService
{
Task<string> Execute(string action);
Task<bool> ValidateScore(string token, double minScore = 0.5);
}
Bune practici
- Lazy loading - Încărcați scriptul reCAPTCHA doar când este necesar
- Error handling - Gestionați cazurile când reCAPTCHA nu este disponibil
- Timeout handling - Setați timeout pentru operațiuni JS
- Score validation - Verificați scorul pe backend pentru reCAPTCHA v3
- Fallback mechanism - Oferiți alternative când captcha eșuează
- Privacy compliance - Informați utilizatorii despre folosirea reCAPTCHA
Concluzie
RecaptchaService oferă o integrare simplă și flexibilă cu Google reCAPTCHA, protejând aplicația împotriva spam-ului și automatizărilor malițioase. Cu suport pentru excluderea utilizatorilor autentificați și configurare flexibilă, serviciul se adaptează ușor la diverse scenarii de utilizare.