У меня есть веб-приложение и проекты веб-API, встроенные в .net 5. Я обновил их обоих до .NET 8.0 и обновил все пакеты в проекте. После обновления до .NET 8 в веб-приложении любой почтовый запрос выдает 400 неверных запросов без использования метода OnPostAsync, он даже не входит в метод. Срабатывает метод OnGet и извлекает данные из базы данных. Каково решение этой проблемы? Я попытался отключить защиту от подделки, и запрос на публикацию перенаправил меня на ту же страницу без публикации данных. Эта проблема возникает только на рабочем сервере: когда я запускаю ее локально на своем устройстве, она работает нормально, я также пытался опубликовать ее на своем локальном сервере IIS, и все работало нормально. Я также попытался запустить его на рабочем сервере с помощью CMD. Я успешно прошел метод post. Я использую протокол HTTP, а не HTTPS.
РЕДАКТИРОВАТЬ
Мой файл web.config:
<?xml version = "1.0" encoding = "utf-8"?>
<configuration>
<location path = "." inheritInChildApplications = "false">
<system.webServer>
<handlers>
<add name = "aspNetCore" path = "*" verb = "*" modules = "AspNetCoreModuleV2" resourceType = "Unspecified" />
<remove name = "OPTIONSVerbHandler" />
<add name = "OPTIONSVerbHandler" path = "*" verb = "GET,HEAD,POST,OPTIONS" modules = "ProtocolSupportModule" resourceType = "Unspecified" requireAccess = "None" />
</handlers>
<aspNetCore processPath = "dotnet" arguments = ".\myapplication.dll" stdoutLogEnabled = "false" stdoutLogFile = ".\logs\stdout" hostingModel = "inprocess" />
</system.webServer>
</location>
</configuration>
<!--ProjectGuid: b2ce28c8-f007-4de5-b3aa-c44c6e651ec5-->
мой файл stratup.cs после удаления program.cs:
using AutoMapper;
using MyApplication.Admin.Data;
using MyApplication.Admin.Middlewares;
using MyApplication.Admin.Model;
using MyApplication.Library.MySQL.DbModels;
using MyApplication.Library.MySQL.Repositories;
using MyApplication.Library.MySQL.Services;
using MyApplication.Library.MySQL.Utility;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using System;
using System.IO;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(44341);
options.Limits.MaxRequestBodySize = 52428800;
});
var services = builder.Services;
var configuration = builder.Configuration;
services.AddDbContext<Context>(options =>
//options.UseSqlServer(
// Configuration.GetConnectionString("ContextConnection"))
//);
options.UseMySQL(
configuration.GetConnectionString("ContextConnection"))
);
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
services.AddHttpClient();
services.AddDbContext<MyContext>();
services.AddScoped<IUnitofWork, UnitofWork>();
services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(1);
options.Lockout.MaxFailedAccessAttempts = 3;
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequiredUniqueChars = 1;
options.Password.RequiredLength = 8;
})
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<Context>();
services.Configure<SecurityStampValidatorOptions>(options =>
{
options.ValidationInterval = TimeSpan.Zero;
});
services.AddRazorPages(options =>
{
//options.Conventions.AuthorizeFolder("/");
}).AddRazorRuntimeCompilation();
services.Configure<RazorViewEngineOptions>(o =>
{
o.ViewLocationFormats.Add("/Pages/Shared/Components/{1}/{0}" + RazorViewEngine.ViewExtension);
});
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new AutoMapperProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddMemoryCache();
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
services.AddRazorPages();
var app = builder.Build();
//Insecure cookie setting: missing Secure flag
app.UseCookiePolicy(
new CookiePolicyOptions
{
Secure = CookieSecurePolicy.Always
});
////Permissions-Policy
//app.Use(async (context, next) =>
//{
// context.Response.Headers.Add("Permissions-Policy", "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()");
// await next();
//});
//Missing security header: X - XSS - Protection
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Xss-Protection", "1");
await next();
});
//Missing security header: X-Content-Type-Options
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
await next();
});
app.UseCors();
////Missing security header: X-Content-Type-Options
//app.Use(async (context, next) =>
//{
// context.Response.Headers.Add("Referrer-Policy", "no-referrer");
// await next();
//});
///////New////
//Missing security header: X - Frame - Options
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
await next();
});
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
//app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(configuration.GetSection("MediaSettings:MEDIAFILESURL").Value, "Media", "Default")),
RequestPath = "/Media/Default"
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// app.UsePermissions();
app.UseAntiforgery();
// global error handler
app.UseMiddleware<ErrorHandler>();
app.MapRazorPages();
app.Run();
моя страница входа в систему.cshtml:
<form id = "account" method = "post">
<div class = "input-group">
<input asp-for = "Input.Email" class = "form-control" placeholder = "Username" />
<span asp-validation-for = "Input.Email" class = "text-danger" style = "margin-top: 2rem;"></span>
</div>
<div class = "input-group">
<input asp-for = "Input.Password" class = "form-control" placeholder = "Password" />
<span asp-validation-for = "Input.Password" class = "text-danger" style = "margin-top: 2rem;"></span>
</div>
<div class = "row kt-login__extra">
<div class = "col">
<label class = "kt-checkbox" asp-for = "Input.RememberMe">
<input asp-for = "Input.RememberMe" />
@Html.DisplayNameFor(m => m.Input.RememberMe)
<span></span>
</label>
</div>
<div class = "col kt-align-right">
<p class = "text-danger">@ViewBag.Message</p>
</div>
</div>
<div class = "kt-login__actions">
<button type = "submit" class = "btn btn-brand btn-elevate kt-login__btn-primary">Log in</button>
</div>
</form>
Файл Login.cshtml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Http;
using System.Net.Http;
using System.Security.Claims;
using MyApplication.Library.MySQL.Utility;
using MyApplication.Library.MySQL.Services;
namespace MyApplication.Admin.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class LoginModel : PageModel
{
private readonly UserManager<IdentityUser> _userManager;
private readonly SignInManager<IdentityUser> _signInManager;
private readonly ILogger<LoginModel> _logger;
private readonly IConfiguration _configuration;
private readonly IUnitofWork _repositories;
public LoginModel(IUnitofWork repositories, SignInManager<IdentityUser> signInManager, ILogger<LoginModel> logger, UserManager<IdentityUser> userManager, IConfiguration configuration)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
_configuration = configuration;
_repositories = repositories;
}
[BindProperty]
public InputModel Input { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
public string ReturnUrl { get; set; }
[TempData]
public string ErrorMessage { get; set; }
public class InputModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
if (User.Identity.IsAuthenticated)
{
Response.Redirect("/Index");
}
if (!string.IsNullOrEmpty(ErrorMessage))
{
ModelState.AddModelError(string.Empty, ErrorMessage);
}
returnUrl ??= Url.Content("~/");
ViewData["AppName"] = ! string.IsNullOrWhiteSpace( _configuration["AppName"]) ? "/" + _configuration["AppName"] : "";
ViewData["Message"] = "";
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
ReturnUrl = returnUrl;
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
ViewData["AppName"] = !string.IsNullOrWhiteSpace(_configuration["AppName"]) ? "/" + _configuration["AppName"] : "";
ViewData["Message"] = "";
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
try
{
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
var userInfo = await _userManager.FindByEmailAsync(Input.Email);
// Create Cookie and Generate new Token
CookieOptions option = new CookieOptions();
//option.Expires = DateTime.Now.AddYears(1);
option.Expires = DateTime.Now.AddDays(10);
// option.Secure = true;
string tokenValue = GenerateNewTokenAsync(userInfo.Id, Input.Email);
Response.Cookies.Append("MyApplication_Token
", tokenValue, option);
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ViewData["Message"] = "Invalid login attempt.";
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
await _signInManager.SignOutAsync();
return Page();
}
}
}
catch (Exception ex) { }
// If we got this far, something failed, redisplay form
return Page();
}
private bool ValidateUserAsync(string UserEmail, string Password)
{
string Result = "";
try
{
var ApiUrl = _configuration["AppSettings:APIURL"];
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
using (HttpClient httpClient = new HttpClient(clientHandler))
{
HttpResponseMessage response = new HttpResponseMessage();
response = httpClient.GetAsync(ApiUrl + "Api/Authenticate/ValidateUser?UserEmail = " + UserEmail + "&Password = " + Password).Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
Result = response.Content.ReadAsStringAsync().Result;
}
if (Result == "true")
return true;
else
return false;
}
}
catch (Exception e)
{
return false;
}
}
private string GenerateNewTokenAsync(string UserId, string UserEmail)
{
string Token = "";
try
{
var ApiUrl = _configuration["AppSettings:APIURL"];
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
using (HttpClient httpClient = new HttpClient(clientHandler))
{
HttpResponseMessage response = new HttpResponseMessage();
response = httpClient.GetAsync(ApiUrl + "Api/Authenticate/GenerateNewToken?UserId = " + UserId + "&UserEmail = " + UserEmail).Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
Token = response.Content.ReadAsStringAsync().Result;
}
}
}
catch (Exception e)
{
return "";
}
return Token;
}
public async Task<JsonResult> OnPostResetPassword(string email)
{
email = email.Trim();
var emails = (await _repositories.AspNetUsers.GetAll()).Select(u => u.Email).ToList();
if (!emails.Contains(email))
{
return new JsonResult(new { status = false, msg = "email not found" });
}
string secret = _configuration["AppSettings:ResetSecret"];
string resetCode = ResetPasswordUtility.GenerateResetPasswordCode();
string resetJwt = ResetPasswordUtility.GenerateResetJwtToken(email, resetCode, secret);
string resetUrl = $"https://localhost:44341/Identity/Account/ResetPassword?ResetToken = {resetJwt}&UserEmail = {email}";
string emailSubject = "MyApplication Reset Password";
string emailBody = $@"
<p>follow this link to reset you password it is valid for 30 minutes only</p>
<br>
<a href = ""{resetUrl}"">{resetUrl}</a>
";
var user = (await _repositories.AspNetUsers.GetAll()).FirstOrDefault(u => u.Email == email);
user.ResetPasswordCode = resetCode;
var saveRes = await _repositories.Save(Guid.Empty);
if (saveRes.Item1 == false)
{
Console.WriteLine(saveRes.Item2);
return new JsonResult(new { status = false, msg = "failed to send email, please try again later" });
}
try
{
EmailUtility.SendEmail(email, emailSubject, emailBody);
return new JsonResult(new { status = true, msg = "please check your email." });
}
catch (System.Exception)
{
return new JsonResult(new { status = false, msg = "failed to send email, please try again later" });
}
}
}
}
можете ли вы показать свой запрос на вызов ajax?
@n_dev Я не делаю никаких ajax-запросов на этой странице, я только вызываю метод OnPostAsync, используя форму.
проверьте проверку свойств модели, затем отправляете ли вы обязательные поля
в модели, которую вы передаете
@n_dev В моей локальной среде (режим разработки) все работает нормально, эта ошибка возникает только тогда, когда я размещаю ее в IIS.
вы проверили свой пул приложений и разрешения в соответствии с iis?
@n_dev Настройки пула приложений следующие: .Net CLR: 4.0 Управляемый конвейер: интегрированный А о каких разрешениях вы говорите?
вы используете хостинг Plesk?
@n_dev у меня есть свой сервер
<system.webServer> <handlers> <remove name = "WebDAV" /> <remove name = "ExtensionlessUrlHandler-Integrated-4.0" /> <remove name = "OPTIONSVerbHandler" /> <remove name = "TRACEVerbHandler" /> <add name = "ExtensionlessUrlHandler-Integrated-4.0" path = "*." verb = "*" type = "System.Web.Handlers.TransferRequestHandler" preCondition = "integratedMode,runtimeVersionv4.0" /> </handlers> <modules> <remove name = "WebDAVModule" /> </modules> </system.webServer>
пожалуйста, попробуйте этот раздел конфигурации для обработки запросов
о боже мой, ты украшаешь свой метод публикации с помощью [HttpPost]
как контроль определяет, что это почтовый запрос
Я попробовал конфигурацию, которую вы рассказали мне выше, и она выдала мне 404, когда я посещаю веб-сайт, для действия публикации это метод OnPostAsync, и я использую бритвенные страницы, элемент управления определит это, когда я добавлю OnPost или OnGet перед именем обработчика. В дополнение к тому, что этот пример работает нормально, как я уже говорил ранее, в моей локальной среде, я сталкиваюсь с проблемами только при публикации его на рабочем сервере IIS.
Я предлагаю вам сначала следовать этой статье, чтобы включить отслеживание неудачных запросов внутри IIS, затем вам следует проанализировать трассировку IIS, чтобы увидеть, обрабатывает ли модуль IIS запрос на публикацию и перенаправляет на страницу по умолчанию или сначала.
Я понял, в чем проблема, Я использую app.UseCookiePolicy, инициализирую класс CookiePolicyOptions и делаю Secure to CookieSecurePolicy. Всегда следующим образом:
app.UseCookiePolicy(
new CookiePolicyOptions
{
Secure = CookieSecurePolicy.Always
});
Для свойства Secure установлено значение CookieSecurePolicy.Always, что означает, что все файлы cookie, созданные приложением, будут отмечены флагом Secure.
Что делает флаг безопасности: Флаг Secure в файле cookie гарантирует, что файл cookie отправляется только через HTTPS (защищенные) соединения. Это предотвращает передачу файла cookie по незашифрованным HTTP-соединениям, что может привести к его перехвату злоумышленниками.
Хотя я запускаю свое приложение по протоколу HTTP «Не защищено», это создает проблему при публикации формы. Если только он не работал нормально в версии .Net 5 «до обновления до 8». Комментарий решил мою проблему.
Какие пакеты? Какой код? можете ли вы показать минимальный воспроизводимый пример ?