我正在尝试注销用户,但 cookie 并未失效。相反,我只是重定向到指定的路径,并且仍然可以访问所有内容。
控制器中的 onLogin 函数与 SignInAsync 工作得很好。
当我在调试中进入它时,ControllerBase.HttpContext.User.Identity.IsAuthenticated 始终为 false。
程序.cs
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using MudBlazor.Services;
using PersonalWebsiteRedesign.Classes.Database;
using PersonalWebsiteRedesign.Components;
using System.Net;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddDbContext<SQL_DB_Context>();
builder.Services.AddControllers();
builder.Services.AddMudServices();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped(sp => new HttpClient(new HttpClientHandler
{
UseCookies = true,
Credentials = CredentialCache.DefaultCredentials
})
{
BaseAddress = new Uri("https://localhost:7043/")
});
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie.Name = "fslr_auth";
options.AccessDeniedPath = "/error";
options.LogoutPath = "/user/logout";
options.LoginPath = "/user/login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.IsEssential = true;
options.Cookie.Path = "/";
options.SlidingExpiration = true;
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Cookies", policy => policy.RequireAuthenticatedUser());
});
builder.Services.AddSingleton<IAuthorizationPolicyProvider, DefaultAuthorizationPolicyProvider>();
builder.Services.AddCascadingAuthenticationState();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// 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.UseAntiforgery();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(PersonalWebsiteRedesign.Client._Imports).Assembly);
app.MapControllers();
app.Run();
AuthController.cs
[ApiController]
public class AuthController : ControllerBase
{
SQL_DB_Context dbContext;
public AuthController(SQL_DB_Context _dbContext)
{
dbContext = _dbContext;
}
[HttpPost]
[Route("api/auth/login")]
public async Task<IActionResult> onLogin([FromBody] LoginUserForm loginUserForm)
{
if (loginUserForm != null && dbContext.Users.Any(x => x.username == loginUserForm.UserName))
{
var userid = dbContext.Users.Where(x => x.username == loginUserForm.UserName).FirstOrDefault().id;
var roleid = dbContext.UserRoles.Where(x => x.id == userid).FirstOrDefault().roleId;
var roleToString = dbContext.RolesList.Where(x => x.id == roleid).FirstOrDefault().Role;
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, loginUserForm.UserName),
new Claim(ClaimTypes.Role, roleToString)
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
return this.Ok();
} else
{
return this.BadRequest();
}
}
[HttpPost]
[Route("api/auth/logout")]
public async Task<IActionResult> onLogout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return this.Ok();
}
注销.razor
page "/user/logout"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Http
@using System.Net
@rendermode InteractiveServer
@attribute [Authorize]
@inject NavigationManager navManager
@inject HttpClient HttpClient
@inject IHttpContextAccessor httpContextAccessor
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
await onLogout();
}
}
private async Task onLogout()
{
var baseAddress = new Uri(navManager.BaseUri);
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
var result = await client.PostAsync("api/auth/logout", null, CancellationToken.None);
if (result.IsSuccessStatusCode)
{
navManager.NavigateTo("/user/login", true);
Console.WriteLine("Logged out user: " + httpContextAccessor.HttpContext.User.Identity.Name + ", Status: " + httpContextAccessor.HttpContext.User.Identity.IsAuthenticated);
}
else
{
Console.WriteLine("Couldn't logout user.");
}
var x = "";
}
}
}
为了确保身份验证 cookie 在注销后失效,您需要从客户端显式清除身份验证 cookie。目前,服务器上的 SignOutAsync 方法会删除身份验证,但 cookie 仍保留在浏览器中,这可能会导致用户仍被识别为已通过身份验证。
这是你应该做的:
cookieContainer.Add(baseAddress, new Cookie(".AspNetCore.Cookies", ""));
这会强制浏览器删除负责用户身份验证的 .AspNetCore.Cookies cookie。
private async Task onLogout()
{
var baseAddress = new Uri(navManager.BaseUri);
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
var result = await client.PostAsync("api/auth/logout", null, CancellationToken.None);
if (result.IsSuccessStatusCode)
{
// Clear the authentication cookie
cookieContainer.Add(baseAddress, new Cookie(".AspNetCore.Cookies", ""));
navManager.NavigateTo("/user/login", true);
Console.WriteLine("Logged out user: " + httpContextAccessor.HttpContext.User.Identity.Name + ", Status: " + httpContextAccessor.HttpContext.User.Identity.IsAuthenticated);
}
else
{
Console.WriteLine("Couldn't logout user.");
}
}
}
这可确保 cookie 从服务器和客户端中删除,从而完全注销用户。