HttpContext.Authentication.SignOutAsync无法删除身份验证cookie。

41
根据ASP.NET Core 文档,方法HttpContext.Authentication.SignOutAsync()必须同时删除身份验证cookie。

退出登录

要退出当前用户并删除其cookie(斜体为本人所加-A.C.),请在控制器内调用以下内容

await HttpContext.Authentication.SignOutAsync("MyCookieMiddlewareInstance");

但实际上它并没有!其他的似乎都没问题,特别是认证方案,因为用户可以正确地登录并创建一个名为.AspNetCore.的cookie。
有任何想法为什么用户退出后cookie仍然存在?

这个今天还有用吗? - Proviste
1
文档稍作更改 - 他们不再使用"MyCookieMiddlewareInstance",而是使用CookieAuthenticationDefaults.AuthenticationScheme,这显然不同。但是,如果代码删除了cookie,你可以很容易地自己检查。毕竟,这是在AccountController(LogOff方法)中调用的,前提是在脚手架应用程序时启用了身份验证。 - Alexander Christov
12个回答

37

根据您发布的代码不足以确定,但我怀疑在您调用SignOutAsync之后,您可能会有某种类型的重定向(例如RedirectToAction),它会覆盖SignOutAsync尝试发出的OIDC endsession URL的重定向。

(与此类似的有关重定向覆盖问题的解释可以在Microsoft的HaoK这里找到。)

编辑:如果我上面的猜测是正确的,则解决方案是在最终的SignOutAsync中使用AuthenticationProperties对象发送重定向URL:

// in some controller/handler, notice the "bare" Task return value
public async Task LogoutAction()
{
    // SomeOtherPage is where we redirect to after signout
    await MyCustomSignOut("/SomeOtherPage");
}

// probably in some utility service
public async Task MyCustomSignOut(string redirectUri)
{
    // inject the HttpContextAccessor to get "context"
    await context.SignOutAsync("Cookies");
    var prop = new AuthenticationProperties()
    {
        RedirectUri = redirectUri
    };
    // after signout this will redirect to your provided target
    await context.SignOutAsync("oidc", prop);
}

完美解决方案。 - Caner
我对 SignOutAsync / ChallengeAsync 扩展方法的设计感到非常沮丧,因为绝对没有任何直接的指示表明消费者不应该在此之后设置自己的 IActionResult。为什么 SignOutAsync 不能返回它自己的 IActionResult 呢?这样消费者就很清楚地知道他们应该传递响应。 - Dai
1
@Dai SignOutAsync 会将客户端重定向到 OIDC 注销终点,因此它不能返回任意的 IActionResult,必须是 OIDC 流程正常工作所需的 HTTP 重定向状态。虽然我希望所有这些东西都能有更好的文档记录,但微软 API 文档现在大多是自动化生成的,留下了很多不足之处。 - McGuireV10
@McGuireV10是正确的,不需要指定RedirectUri就可以正确重定向。这个响应中的重点是有两个SignOutAsync的调用;一旦我调用了await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme),它就会删除cookie。 - Marius Ologesa
2
对我来说似乎不起作用,它重定向到“/SomeOtherPage”,但cookie仍然存在。 - Denny

9

我最近也遇到了同样的问题。在我的情况下,浏览器创建了多个cookie。其中一个的名称类似于“.AspNetCore.Antiforgery”,另外一个是我在startup.cs中设置的自定义名称。

对我解决错误的是JTvermose的答案的第一部分,稍作修改即可。我在我的注销方法中添加了下面的代码。 一切都很顺利。

    if (HttpContext.Request.Cookies.Count> 0) 
        {
            var siteCookies = HttpContext.Request.Cookies.Where(c => c.Key.Contains(".AspNetCore.") || c.Key.Contains("Microsoft.Authentication"));
            foreach (var cookie in siteCookies)
            {
                Response.Cookies.Delete(cookie.Key);
            }
        }

                await HttpContext.SignOutAsync(
    CookieAuthenticationDefaults.AuthenticationScheme);
        HttpContext.Session.Clear();
        return RedirectToPage("/Index");

4

我有同样的问题。 SignOutAsync没有按照预期工作。

我找到了这个:

Response.Cookies.Delete(".AspNetCore.<nameofcookie>");

3
在哪里?是在源代码中还是其他地方(博客、答案等)?而且,尽管代码更短,但魔术字符串确实很讨厌。 - Alexander Christov
@AlexanderChristov 什么是魔术字符串? - David Klempfner
1
@DavidKlempfner 硬编码的字符串被认为是"魔法"字符串。相反,您可以使用常量、资源等来摆脱它们,因为您可能会轻易拼写错误一个,或者在需要更改时忘记修改它。 - Alexander Christov

3

通过这一行代码解决了问题。

最初的回答
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
// await _SignInManager.SignOutAsync();
// HttpContext.Response.Cookies.Delete(".AspNetCore.Cookies");

2

我用以下代码解决了删除网站cookies的问题,将其放置在我的控制器Logout()方法中。我发现我的网站会创建多个cookie。

// Delete the authentication cookie(s) we created when user signed in
            if (HttpContext.Request.Cookies[".MyCookie"] != null)
            {
                var siteCookies = HttpContext.Request.Cookies.Where(c => c.Key.StartsWith(".MyCookie"));
                foreach (var cookie in siteCookies)
                {
                    Response.Cookies.Delete(cookie.Key);
                }
            }

在 Startup.cs 中:

app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                AuthenticationScheme = "Cookies",
                LoginPath = new PathString("/Account/Login/"),
                AccessDeniedPath = new PathString("/Home/Index/"),
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                CookieName = ".MyCookie"
            });

请注意,我不使用 await HttpContext.Authentication.SignOutAsync("MyCookieMiddlewareInstance");,因为我正在使用与Google的OpenIdConnect。

我猜只需要删除一个 cookie(即认证 cookie)。删除您网站放置的所有 cookie 似乎是一种非常粗暴和可能不太理想的方法,尽管它可能有效。而删除单个 cookie 看起来就像 Castro JR 已经建议的解决方案,你看到有什么区别吗?无论如何,谢谢。 - Alexander Christov

2
这是删除cookie的代码(如果没有其他办法,就使用蛮力):
await this.HttpContext.Authentication.SignOutAsync(<AuthenticationScheme>);

// ...

var cookie = this.Request.Cookies[<CookieName>];
if (cookie != null)
{
    var options = new CookieOptions { Expires = DateTime.Now.AddDays(-1) };
    this.Response.Cookies.Append(cookieName, cookie, options);
}

糟糕,糟糕,糟糕!看起来是一个非常丑陋的补丁!但是能够工作... :(

还有其他解决方案吗?


1
也许您的情况是使用ASP.NET Core身份验证,就像我的情况一样。因此,您可以尝试使用ASP NET Identity的API,而不是使用HttpContext.Authentication.SignOutAsync()或HttpContext.SignOutAsync()。

SignInManager.SignOutAsync()

API从HttpContext未清除以".AspNetCore."开头的cookie。
(要使用SignInManager,您需要通过asp net core的DI引入SignInMbanager)

1
我正在使用signInManager.SignOutAsync(),然后使用signInManager.SignInAsync(identityUser, true)登录不同的用户。一切似乎看起来都很好,但实际上CurrentUser并没有改变!当我回到我的Angular应用程序并重新加载页面时,我仍然看到之前登录的用户。我需要做的是在不注销的情况下登录不同的用户(通过Angular UI中的下拉菜单和调用应用程序服务方法来切换到不同的用户)。 - Alexander

1

这个会像魔法一样完成工作。

await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
HttpContext.Session.Clear();

0
在我的情况下,这行代码有效 -
await HttpContext.SignOutAsync("Identity.Application");

没错。您也可以使用 await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme); - Augis

0
如果你想让这个代码 if(HttpContext.User.Identity.IsAuthenticated) 的结果变成 false,那么你可以这样做:HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>()));

目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接