我有一个 ASP.NET Core Razor Pages 网页,可以在智能手机(Android 和 iPhone)上浏览。
我使用 ASP.NET Core Identity 来保护对网站的访问。
我的用户希望将图标添加到手机的主屏幕上,以便快速访问该网站(使用移动版本网络浏览器的“将图标添加到主屏幕”功能,该功能显然会“安装”渐进式 Web 应用程序(如果已配置)与清单)。 我无法让它正常工作。 使用主屏幕按钮访问网站时,浏览器似乎不会加载本地网站数据,尤其是 cookie。 如果缺少 ASP.NET Identity Cookie,服务器将重定向到登录页面。 如果我输入凭据并发布表单,服务器会响应
400 Bad Request
。
日志文件摘录:
2024-12-10 15:00:42.8269|1|INFO|Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.AutoValidateAntiforgeryTokenAuthorizationFilter|Antiforgery token validation failed. The provided antiforgery token was meant for a different claims-based user than the current user. Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The provided antiforgery token was meant for a different claims-based user than the current user.
at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)
at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext)
at Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)
防伪令牌的拒绝仅发生在上述上下文中,而不是其他地方。
这个问题我已经困扰很长时间了。我尝试解决该问题的事情:
manifest.json
来配置渐进式 Web 应用程序安装。WebEssentials.AspNetCore.PWA
。以上都没有帮助我解决这个问题。
清单:
{
"short_name": "short name",
"name": "name",
"start_url": "/",
"scope": "/",
"display": "browser",
"orientation": "portrait",
"dir": "ltr",
"lang": "de-DE",
"theme_color": "#6FC2FF",
"background_color": "#FFFFFF",
"icons": [
{
"src": "/icons/rageguy512.png",
"type": "image/png",
"sizes": "512x512",
"purpose": "any"
},
{
"src": "/icons/rageguy192.png",
"type": "image/png",
"sizes": "192x192",
"purpose": "any"
}
]
}
我的_Layout.cshtml中的参考:
<link rel="manifest" href="/manifest.json">
程序.cs
using WebEssentials.AspNetCore.Pwa;
// ...
builder.Services.AddProgressiveWebApp(new PwaOptions()
{
Strategy = ServiceWorkerStrategy.Minimal,
});
// ...
控制器(LoginAsync()),抛出上面提到的400:
using ***.Model.Identity;
using ***.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
namespace ***.Areas.Identity.Controllers
{
[Route("[Controller]/[Action]")]
[Area("Identity")]
[AutoValidateAntiforgeryToken]
public class AccountController : Controller
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IUserStore<ApplicationUser> _userStore;
private readonly UserManager<ApplicationUser> _userManager;
private readonly ReturnUrlService _returnUrlService;
public AccountController(SignInManager<ApplicationUser> signInManager,
IUserStore<ApplicationUser> userStore,
UserManager<ApplicationUser> userManager,
ReturnUrlService returnUrlService)
{
_signInManager = signInManager;
_userStore = userStore;
_userManager = userManager;
_returnUrlService = returnUrlService;
}
[TempData]
public string? ErrorMessage { get; set; }
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> LoginAsync([FromForm] LoginInputModel Input)
{
if (ModelState.IsValid)
{
ApplicationUser? user = await _signInManager.UserManager.FindByIdAsync(Input.Id!);
if (user == null)
return RedirectToPage("/Account/Login");
var result = await _signInManager.PasswordSignInAsync(user, Input.Password!, true, lockoutOnFailure: true);
if (result.Succeeded)
{
return LocalRedirect(_returnUrlService.UrlUnescaped());
}
if (result.IsLockedOut)
{
return RedirectToPage("/Account/Lockout");
}
else
{
TempData.Set("ErrorMessage", "Login-Versuch ungültig!");
return RedirectToPage("/Account/Login");
}
}
TempData.Set("ErrorMessage", "Fehler!");
return RedirectToPage("/Account/Login");
}
public class LoginInputModel
{
[Required, StringLength(200, ErrorMessage = "Maximal 200 Zeichen.")]
[Display(Name = "Benutzer")]
public string? Id { get; set; }
[Required(ErrorMessage = "Es muss ein Passwort angegeben werden.")]
[DataType(DataType.Password)]
[Display(Name = "Passwort")]
public string? Password { get; set; }
}
[HttpPost]
[Authorize(Policy = "TimeRecordingPolicy")]
public async Task<IActionResult> LogoutAsync()
{
await _signInManager.SignOutAsync();
return RedirectToPage("/Account/Login");
}
}
}
任何帮助表示赞赏。
我发现了问题: 显然,在渐进式 Web 应用程序的上下文中,将
samesite-attribute
设置为 strict
的 cookie 处理得不好。我将 ASP.NET Identity Cookie 配置为具有 samesite=lax
,问题就消失了。
Program.cs 摘录:
builder.Services.ConfigureApplicationCookie(o =>
{
o.LoginPath = "/Identity/Account/Login";
o.LogoutPath = "/Identity/Account/Logout";
o.AccessDeniedPath = "/Identity/Account/AccessDenied";
o.Cookie.MaxAge = TimeSpan.FromDays(6);
o.Cookie.HttpOnly = true;
o.Cookie.SameSite = SameSiteMode.Lax; // <=====
o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
o.ExpireTimeSpan = TimeSpan.FromDays(6);
o.SlidingExpiration = true;
});