Введение
Данный шаблон поможет вам добавить защиту формы с помощью Google reCaptcha в ваше SPA приложение. Кроме того, используется прокси пакет для одновременного запуска SPA и .NET приложений. Данная статья основана на статье Adding CAPTCHA on form posts with ASP.NET Core. Если вам нужна защита на Razor pages, то используйте ее.
Создание .NET приложения
Я использую Microsoft Visual Studio 2022 в случае использования других версий экраны и код будут немного отличатся.
Создайте проект ASP .NET Core Web API.

Выберите подходящее имя

И настройте опции как на этом экране, впрочем, это мой выбор для упрощения тестового проекта.

Далее удалите лишние файлы, созданные мастером.
Добавьте следующие пакеты:
-
Microsoft.AspNetCore.SpaServices.Extensions
-
Newtonsoft.Json
Файл Program.cs после правок
using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer; using VueRecaptcha.Services; namespace VueRecaptcha { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); builder.Services.AddHttpClient<ReCaptcha>(x => { x.BaseAddress = new Uri("https://www.google.com/recaptcha/api/siteverify"); }); builder.Services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/build"; }); builder.Services .AddMvc() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }); var app = builder.Build(); // Configure the HTTP request pipeline. app.UseStaticFiles(); app.UseSpaStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllers(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action=Index}/{id?}"); }); app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; spa.UseReactDevelopmentServer(npmScript: "serve"); }); app.Run(); } } }
Файл ReCaptcha.cs
using Newtonsoft.Json.Linq; namespace VueRecaptcha.Services; public class ReCaptcha { private readonly HttpClient _captchaClient; private readonly ILogger<ReCaptcha> _logger; private readonly IConfiguration _configuration; public ReCaptcha(HttpClient captchaClient, ILogger<ReCaptcha> logger, IConfiguration config) { _captchaClient = captchaClient; _logger = logger; _configuration = config; } public string GetClientMarkup() { var siteKey = _configuration.GetValue<string>("SiteKey"); return siteKey; } public async Task<bool> IsValid(string captcha) { try { var secretKey = _configuration.GetValue<string>("SecretKey"); var postTask = await _captchaClient .PostAsync($"?secret={secretKey}&response={captcha}", new StringContent("")); var result = await postTask.Content.ReadAsStringAsync(); var resultObject = JObject.Parse(result); dynamic success = resultObject["success"]; return (bool)success; } catch (Exception e) { _logger.LogError("Failed to validate",e); return false; } } }
Файл UploadModel.cs
namespace VueRecaptcha.ViewModels; public class UploadModel { public string Email { get; set; } = ""; public string Name { get; set; } = ""; public string CaptchaResponse { get; set; } = ""; public List<IFormFile> Files { get; set; } = new List<IFormFile>(); }
Создание SPA приложения
В папке .NET приложения выполните команду
vue create clientapp
укажите что используете vue 3
перейдите в папку clientapp
добавьте поддержку typescript
vue add typescript
Установите пакеты
npm i -S axios
настройте проект
vue.config.js
const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ devServer: { onAfterSetupMiddleware() { // Output the same message as the react dev server to get the Spa middleware working with vue. console.info("Starting the development server..."); } }, transpileDependencies: true })
HelloWorld.vue
<template> <div class="hello"> <div v-if="uploaded">Файлы загружены</div> <form class="uploadForm" v-else> <label for="email" >Email <input type="email" id="email" name="email" required v-model="email" /> </label> <label for="name" >Name <input type="text" id="name" name="name" required v-model="name" /> </label> <input type="file" multiple hidden id="upload_files" /> <div class="g-recaptcha" :data-sitekey="siteKey"></div> <button @click="addFiles()">Add files</button> <button type="submit" @click.prevent="submit">Submit</button> </form> </div> </template> <script lang="ts"> import { Vue } from "vue-class-component"; import axios from "axios"; export default class HelloWorld extends Vue { siteKey: string = ""; email: string = ""; name: string = ""; uploaded: boolean = false; async submit() { var formData = new FormData(); // добавляем в форму поля formData.append("email", this.email); formData.append("name", this.name); // получаем значение капчи const resp = ( document.getElementsByName( "g-recaptcha-response" )[0] as HTMLTextAreaElement ).value; formData.append("captchaResponse", resp); // поле загрузки файлов var imagefile = document.getElementById("upload_files") as HTMLInputElement; if (imagefile == null) return; if (imagefile.files == null) return; const files = imagefile.files; for (let i = 0; i < files.length; i++) { // добавляем в форму выбранные файлы formData.append("files", files[i]); } var result = await axios.post("/api/upload", formData); if (result) { console.log(result); this.uploaded = true; } return false; } addFiles() { const upload = document.getElementById("upload_files"); upload?.click(); } async mounted() { // я сделал так что бы все настройки хранились на сервере, если не нравится лишний запрос то захардкодьте это значение var captcha = await axios.get("/api/upload"); this.siteKey = captcha.data.toString(); this.createRecaptcha(); } createRecaptcha() { var s = document.createElement("script"); s.setAttribute("src", "https://www.google.com/recaptcha/api.js"); s.async = true; s.defer = true; document.body.appendChild(s); } } </script> <style scoped> a { color: #42b983; } .uploadForm { margin: 20px auto; width: 300px; display: block; } .uploadForm label { display: block; width: 100%; margin-bottom: 10px; } </style>
Получение своих ключей
Войдите в свой аккаунт reCAPTCHA
https://www.google.com/recaptcha/about/
Добавьте новый сайт, для целей локального тестирования не забудьте добавить localhost
Выберите reCAPTCHA v2 Site Key и Secret Key, скопируйте в файл appsettings.json
И можно запускать отладку приложения в Visual Studio.
Заключение
В итоге мы научились загружать несколько файлов из SPA приложения на бэкенд с проверкой капчей.
Успехов вам и меньше спэма!
Исходный код
ссылка на оригинал статьи https://habr.com/ru/post/673552/
Добавить комментарий