FormsAuthentication.SignOut()不会注销用户


143

我把头砸得太久了。使用FormsAuthentication.SignOut注销后,如何防止用户浏览网站的页面?我希望这样做:

FormsAuthentication.SignOut();
Session.Abandon();
FormsAuthentication.RedirectToLoginPage();

但事实并非如此。如果直接输入URL,我仍然可以浏览到该页面。我已经有一段时间没有使用过自助式安全了,所以我忘记了为什么这不起作用。


该代码按原样就可以了……在浏览器中单击以返回并不会重新访问服务器上的页面,而只是重新加载页面的本地缓存版本。下面的所有解决方案似乎都忽略了这一事实,实际上没有做任何比您在此做的事情更多的事情。简而言之...对于这个问题,没有任何答案可以解决用户迄今为止查看其缓存的问题,我不相信有一种方法可以说出... js或服务器端指令来清除缓存。
战争

此答案提供了一些检查方法,尤其是如果您的网站未通过PEN测试:stackoverflow.com/questions/31565632/…–
泰勒·S·罗珀

Answers:


211

用户仍然可以浏览您的网站,因为在您致电时Cookie不会被清除,FormsAuthentication.SignOut()并且每次新请求都会对它们进行身份验证。在MS文档中,会说cookie将被清除,但是不会清除,是吗?与完全相同Session.Abandon(),cookie仍然存在。

您应该将代码更改为此:

FormsAuthentication.SignOut();
Session.Abandon();

// clear authentication cookie
HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie1.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie1);

// clear session cookie (not necessary for your current problem but i would recommend you do it anyway)
SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState");
HttpCookie cookie2 = new HttpCookie(sessionStateSection.CookieName, "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);

FormsAuthentication.RedirectToLoginPage();

HttpCookieSystem.Web名称空间中。MSDN参考


18
这对我有用。但是,值得注意的是,如果在登录时在FormsAuthentication cookie上设置了Domain属性,则在注销时在cookie到期时也需要设置该属性
Phil

8
同样不要忘记cookie1.HttpOnly = true;
德米特里·扎耶特

6
这对我来说似乎是一个更好的解决方案:Response.Cookies [FormsAuthentication.FormsCookieName] .Expires = DateTime.Now.AddDays(-1);
兰迪·H

7
@RandyH。用新的空Cookie覆盖现有的FormsAuthentication cookie可以确保即使客户端回退其系统时钟,他们仍将无法从cookie中检索任何用户数据。
Tri Q Tran

9
有人可以将所有这些评论合并为答案吗?
大卫

22

使用x64igor和Phil Haselden的上述两个帖子解决了这个问题:

1. x64igor给出了注销示例:

  • 您首先需要通过将“响应”中的空Cookie传递回“注销”中来清除“身份验证Cookie”和“会话Cookie”

    public ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        Session.Clear();  // This may not be needed -- but can't hurt
        Session.Abandon();
    
        // Clear authentication cookie
        HttpCookie rFormsCookie = new HttpCookie( FormsAuthentication.FormsCookieName, "" );
        rFormsCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rFormsCookie );
    
        // Clear session cookie 
        HttpCookie rSessionCookie = new HttpCookie( "ASP.NET_SessionId", "" );
        rSessionCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rSessionCookie );

2. Phil Haselden给出了上面的示例,说明如何在注销后防止缓存:

  • 您需要通过Response使客户端上的缓存无效

        // Invalidate the Cache on the Client Side
        Response.Cache.SetCacheability( HttpCacheability.NoCache );
        Response.Cache.SetNoStore();
    
        // Redirect to the Home Page (that should be intercepted and redirected to the Login Page first)
        return RedirectToAction( "Index", "Home" ); 
    }

1
为了解决这个问题,我们整天都在工作。登录后,注销按钮开始调用控制器中的错误操作(登录而不是注销)。谢谢,这解决了问题。开发环境:ASP.NET 4.51 MVC 5.1
Ako 2014年

1
好答案!卑微建议:使用格式清除x64igor使用的会话cookie SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState"); HttpCookie sessionCookie = new HttpCookie(sessionStateSection.CookieName, "");。通常,会话cookie名称不是"ASP.NET_SessionId"
seebiscuit

20

在我看来,您没有在中正确设置web.config授权部分。请参阅下面的示例。

<authentication mode="Forms">
  <forms name="MyCookie" loginUrl="Login.aspx" protection="All" timeout="90" slidingExpiration="true"></forms>
</authentication>
<authorization>
  <deny users="?" />
</authorization>

这是一个简单得多的解决方案,我将其标记为答案。当我在一个服务器上的不同服务器上使用一种版本的代码时,我不需要设置您在此处添加的其他属性,而在其他服务器上则需要这样做。因此,修改代码不应该是正确的解决方案,修改配置更好。
弗拉基米尔·博济奇

默认情况下,slidingExpiration设置为true(msdn.microsoft.com/library/1d3t3c61 (v=vs.100).aspx )。这最终将导致cookie在超时设置的x分钟后失效,而不是在通过SignOut()注销用户时失效。因此,这不会导致使用FormsAuthentication注销用户所需的行为。如果我错了,请纠正我。
OlafW

12

此处的关键是您说“如果我直接输入URL ...”。

默认情况下,在表单身份验证下,浏览器为用户缓存页面。因此,直接从浏览器的地址框下拉列表中选择一个URL或输入URL,可以从浏览器的缓存中获取页面,而永远不要回到服务器检查身份验证/授权。解决方案是防止在每个页面的Page_Load事件或基础页面的OnLoad()中进行客户端缓存:

Response.Cache.SetCacheability(HttpCacheability.NoCache);

您可能还想致电:

Response.Cache.SetNoStore();

11

我以前也为此感到苦恼。

这是似乎正在发生的事情的类比...新访客Joe来到该站点,并使用FormsAuthentication通过登录页面登录。ASP.NET为Joe生成一个新的身份,并给他一个cookie。那个小甜饼就像房子的钥匙,只要乔用那个钥匙回来,他就可以打开锁。每个访问者都有一个新的密钥和一个新的锁可以使用。

FormsAuthentication.SignOut()被调用时,系统会告诉乔失去的关键。通常,这可行,因为Joe不再拥有密钥,所以他无法进入。

但是,如果Joe回来并且确实丢失了钥匙,他将被放回原处!

据我所知,没有办法告诉ASP.NET更改门上的锁!

我可以忍受的方式是在Session变量中记住Joe的名字。当他注销时,我放弃了Session,所以不再有他的名字。稍后,要检查是否允许他进入,我只需将其Identity.Name与当前会话中的内容进行比较,如果它们不匹配,则他不是有效的访客。

简而言之,对于一个网站,不要不User.Identity.IsAuthenticated检查会话变量就不要依赖它!


8
+ 1,我认为这称为“ Cookie重播攻击”。关于FormsAuthentication.SignOut的限制,有一篇文章:support.microsoft.com/kb/900111
Dmitry

3
对于任何希望遵循上面链接的人,它已经死了。您可以尝试使用WaybackMachine在此处获取此页面的副本,但是它会立即尝试重定向用户。web.archive.org/web/20171128133421/https://...
Killa的字节

7

经过大量的搜索,最终这对我有用。希望对您有所帮助。

public ActionResult LogOff()
{
    AuthenticationManager.SignOut();
    HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
    return RedirectToAction("Index", "Home");
}

<li class="page-scroll">@Html.ActionLink("Log off", "LogOff", "Account")</li>

我多年来一直在用PHP开发Web应用程序。所以我是MVC的新手...我承认我喜欢它,但是谁会想到像注销某人这样简单的事情会如此困难?我在此页面上尝试了所有其他脚本,这是唯一起作用的脚本。感谢您的发布!
安东尼·格里格斯

6

这对我有用

public virtual ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        foreach (var cookie in Request.Cookies.AllKeys)
        {
            Request.Cookies.Remove(cookie);
        }
        foreach (var cookie in Response.Cookies.AllKeys)
        {
            Response.Cookies.Remove(cookie);
        }
        return RedirectToAction(MVC.Home.Index());
    }

3

您发布的代码看起来应该正确删除了表单身份验证令牌,因此所讨论的文件夹/页面可能实际上未受到保护。

您是否已确认登录之前无法访问页面?

您可以发布您正在使用的web.config设置和登录代码吗?


3

我一直在为我的所有Pages编写基类,但遇到了同样的问题。我有类似以下的代码,但没有用。通过跟踪,控制从RedirectToLoginPage()语句传递到下一行,而无需重定向。

if (_requiresAuthentication)
{
    if (!User.Identity.IsAuthenticated)
        FormsAuthentication.RedirectToLoginPage();

    // check authorization for restricted pages only
    if (_isRestrictedPage) AuthorizePageAndButtons();
}

我发现有两种解决方案。要么修改FormsAuthentication.RedirectToLoginPage(); 成为

if (!User.Identity.IsAuthenticated)
    Response.Redirect(FormsAuthentication.LoginUrl);

或通过添加来修改web.config

<authorization>
  <deny users="?" />
</authorization>

在第二种情况下,在跟踪时,控件没有到达请求的页面。在达到断点之前,它已立即重定向到登录URL。因此,SignOut()方法不是问题,重定向方法是唯一的问题。

我希望可以帮助某人

问候


2
您也可以在调用FormsAuthentication.RedirectToLoginPage()之后立即调用Response.End()
murki

我认为MS方面有些沟通不畅。如果要让他们返回登录页面,则必须将他们拒之门外。否则,框架将使您高兴。因此,您必须在这篇文章中说第二种解决方案。
乔什·罗宾逊

3

我只是在这里尝试了一些建议,虽然可以使用浏览器的后退按钮,但是当我单击菜单选项时,该[ActionResult]的[Authorize]令牌立即将我发送回了登录屏幕。

这是我的注销代码:

        FormsAuthentication.SignOut();
        Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
        Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
        HttpCookie cookie = HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (cookie != null)
        {
            cookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Add(cookie);
        }

尽管浏览器上的后退功能使我退回并显示了安全菜单(我仍在处理该菜单),但我无法执行该应用程序中已保护的任何操作。

希望这可以帮助


谢谢。这是对我<deny users="?" />有用的解决方案(不需要在web.config中使用)
Alexei

3

我已经尝试过该线程中的大多数答案,没有运气。最终这样:

protected void btnLogout_Click(object sender, EventArgs e)
{
    FormsAuthentication.Initialize();
    var fat = new FormsAuthenticationTicket(1, "", DateTime.Now, DateTime.Now.AddMinutes(-30), false, string.Empty, FormsAuthentication.FormsCookiePath);
    Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)));
    FormsAuthentication.RedirectToLoginPage();
}

在这里找到它:http : //forums.asp.net/t/1306526.aspx/1


3

该答案在技术上与Khosro.Pakmanesh相同。我将其发布,以阐明他的答案与该线程上的其他答案有何不同,以及可以在哪种使用情况下使用。

一般来说要清除用户会话,在做

HttpContext.Session.Abandon();
FormsAuthentication.SignOut();

将有效注销用户。但是,如果在同一请求中需要检查Request.isAuthenticated(例如,在“授权过滤器”中可能经常发生),那么您会发现

Request.isAuthenticated == true

甚至_after你做HttpContext.Session.Abandon()FormsAuthentication.SignOut()

唯一有效的是做

AuthenticationManager.SignOut();
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);

有效地设置Request.isAuthenticated = false


2

这开始发生在我身上时,我设置了认证>表格> Path属性Web.config。删除该文件可以解决问题,然后简单地FormsAuthentication.SignOut();删除该cookie。


1

可能是您从一个子域(sub1.domain.com)登录,然后尝试从另一个子域(www.domain.com)注销。


1

我只是遇到了同样的问题,其中SignOut()似乎无法正确删除票证。但仅在特定情况下,其中某些其他逻辑导致重定向。我删除了第二个重定向(将其替换为错误消息)后,问题消失了。

问题一定是页面在错误的时间重定向,因此没有触发身份验证。


1

我现在有一个类似的问题,我认为我的问题以及原始张贴者的问题都是由于重定向。默认情况下,Response.Redirect会导致一个异常,该异常立即冒泡直到被捕获并立即执行重定向,我猜测这将阻止修改后的Cookie集合向下传递给客户端。如果您修改代码以使用:

Response.Redirect("url", false);

这样可以防止出现异常,并且似乎允许将Cookie正确发送回客户端。


1

只需在按下登录键时尝试发送会话变量。然后在欢迎页面上,首先检查该会话在页面加载或初始化事件中是否为空:

if(Session["UserID"] == null || Session["UserID"] == "")
{
    Response.Redirect("Login.aspx");
}

1

对我而言,以下方法有效。我认为,如果在“ FormsAuthentication.SignOut()”语句之后出现任何错误,SingOut将不起作用。

public ActionResult SignOut()
    {
        if (Request.IsAuthenticated)
        {
            FormsAuthentication.SignOut();

            return Redirect("~/");
        }
        return View();
     }

0

您是否正在使用IE测试/查看此行为?IE可能会从缓存中提供这些页面。众所周知,很难让IE刷新其缓存,因此在许多情况下,即使注销后,键入“受保护”页面之一的url也会显示以前的缓存内容。

(即使您以其他用户身份登录,我也已经看到了这种行为,IE会在页面顶部显示“ Welcome”栏,其中包含旧用户的用户名。如今,通常重新加载会更新它,但是如果它是持久性的,这仍然可能是一个缓存问题。)


0

进行Session.abandon()并销毁cookie效果很好。我正在使用mvc3,如果您进入受保护的页面,注销并通过浏览器历史记录浏览,似乎会出现问题。没什么大不了,但还是有点烦人。

不过,尝试通过我的Web应用程序上的链接是正确的方法。

将其设置为不进行浏览器缓存可能是可行的方法。


0

对于MVC,这对我有效:

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return Redirect(FormsAuthentication.GetRedirectUrl(User.Identity.Name, true));
        }

0

我想添加一些信息以帮助理解问题。表单身份验证允许将用户数据存储在cookie或URL的查询字符串中。您的站点支持的方法可以在web.config文件中进行配置。

根据微软的说法

如果CookiesSupported为false,则 SignOut方法将从cookie或URL中删除表单身份验证票证信息。

同时,他们说

HttpCookieMode值之一,该值指示是否为无cookie表单身份验证配置了应用程序。 默认情况下是UseDeviceProfile

最后,关于UseDeviceProfile,他们说

如果CookieMode属性设置为UseDeviceProfile,则如果 当前请求的浏览器同时支持cookie和使用cookie的重定向,则 CookiesSupported属性将返回true。否则,CookiesSupported属性将返回false。

将所有这些拼凑在一起,具体取决于用户的浏览器,默认配置可能会导致CookiesSupported为true,这意味着SignOut方法不会从cookie中清除票证。这似乎违反直觉,而且我不知道为什么会这样工作-我希望SignOut在任何情况下都能实际注销用户。

一种使SignOut本身起作用的方法是在web.config文件中将cookie模式更改为“ UseCookies”(即,需要cookie):

<authentication mode="Forms">
  <forms loginUrl="~/Account/SignIn" cookieless="UseCookies"/>
</authentication>

根据我的测试,这样做会使SignOut自行工作,而这需要您的站点付出代价,现在需要cookie才能正常运行。


我认为您读错了。关于SignOut(),我很确定它们的意思是,如果CookiesSupported为false,将从URL中清除它,否则从cookie中清除它。也就是说,他们应该已经编写了“ SignOut方法从cookie或从URL删除(如果CookiesSupported为false的)表单验证票据信息。”
Oskar Berggren

-1

请注意,如果STS中的wsignoutcleanup消息与IIS中的应用程序名称不匹配,则WIF 拒绝告诉浏览器清理cookie,我的意思是CASE SENSITIVE。WIF会以绿色的OK确认回应,但不会将删除Cookie的命令发送到浏览器。

因此,您需要注意网址的区分大小写。

例如,ThinkTecture Identity Server将访问的RP的URL保存在一个cookie中,但是使它们全部变为小写。WIF将收到小写的wsignoutcleanup消息,并将其与IIS中的应用程序名称进行比较。如果不匹配,它将不删除任何cookie,但会向浏览器报告“确定”。因此,对于此Identity Server,我需要将所有URL都写在web.config中,并将所有应用程序名称写在IIS中,以小写形式,以避免出现此类问题。

另外,如果您的应用程序位于STS子域之外,也不要忘记在浏览器中允许第三方cookie,否则即使WIF告知,浏览器也不会删除这些cookie。


1
WIF?STS?ThinkTecture Identity Server?这些都是什么东西,它们与这个问题有什么关系?
Oskar Berggren
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.