【问题标题】:FormsAuthentication.SignOut() does not log the user outFormsAuthentication.SignOut() 不会将用户注销
【发布时间】:2010-09-29 13:32:18
【问题描述】:

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

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

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

【问题讨论】:

  • 该代码原样正常...在浏览器中单击返回不会重新访问服务器上的页面,它只是重新加载页面的本地缓存版本。下面的所有解决方案似乎都忽略了这一事实,实际上并没有做比你在这里做的更多的事情。简而言之......这个问题没有答案可以解决用户查看他们的缓存到目前为止我不相信有一种方法可以清除缓存中说...... js或服务器端指令。
  • 这个答案提供了一些检查方法,特别是如果您的网站未通过 PEN 测试:stackoverflow.com/questions/31565632/…

标签: asp.net forms-authentication


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    在我看来,您的 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 注销用户所需的行为。如果我错了,请纠正我。
    【解决方案3】:

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

    (即使您以其他用户身份登录,我也看到过这种行为,并且 IE 在您的页面顶部显示“欢迎”栏,并带有旧用户的用户名。现在,通常重新加载会更新它,但是如果它是持久的,它仍然可能是一个缓存问题。)

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        我一直在为我的所有页面编写一个基类,我遇到了同样的问题。 我有如下代码,但它不起作用。通过跟踪,控制从 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() 方法不是问题,重定向方法才是问题所在。

        希望对大家有所帮助

        问候

        【讨论】:

        • 你也可以在调用 FormsAuthentication.RedirectToLoginPage() 之后调用 Response.End()
        • 我认为 MS 方面存在一些误解。如果您希望他们返回登录页面,您必须将他们锁定。否则,该框架将兴高采烈地允许您访问。所以你必须在这篇文章中说解决方案#2。
        【解决方案6】:

        用户仍然可以浏览您的网站,因为当您调用 FormsAuthentication.SignOut() 时不会清除 cookie,并且它们会在每个新请求时进行身份验证。在 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();
        

        HttpCookie 位于 System.Web 命名空间中。 MSDN Reference.

        【讨论】:

        • 这对我有用。但是值得注意的是,如果在登录时已经在 FormsAuthentication cookie 上设置了 Domain 属性,那么在您注销时 cookie 到期时也需要设置它
        • 也别忘了cookie1.HttpOnly = true;
        • 这对我来说似乎是一个更好的解决方案:Response.Cookies[FormsAuthentication.FormsCookieName].Expires = DateTime.Now.AddDays(-1);
        • @RandyH。用新的空 cookie 覆盖现有的 FormsAuthentication cookie 可确保即使客户端调回系统时钟,他们仍然无法从 cookie 中检索任何用户数据。
        • 有人可以将所有这些 cmets 组合成答案吗?
        【解决方案7】:

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

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

        【讨论】:

          【解决方案8】:

          这里的关键是你说“如果我直接输入一个 URL……”。

          默认情况下,在表单身份验证下,浏览器会为用户缓存页面。因此,直接从浏览器地址框下拉列表中选择一个 URL,或者输入它,可以从浏览器的缓存中获取该页面,并且永远不会返回服务器检查身份验证/授权。解决这个问题的方法是防止客户端在每个页面的 Page_Load 事件中缓存,或者在您的基本页面的 OnLoad() 中缓存:

          Response.Cache.SetCacheability(HttpCacheability.NoCache);
          

          您可能还想致电:

          Response.Cache.SetNoStore();
          

          【讨论】:

            【解决方案9】:

            我以前也遇到过这个问题。

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

            FormsAuthentication.SignOut() 被调用时,系统告诉乔丢失密钥。通常,这是可行的,因为乔不再拥有钥匙,他无法进入。

            但是,如果乔回来了,并且确实有那把丢失的钥匙,他会被放回去!

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

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

            简而言之,对于一个网站,不要依赖 User.Identity.IsAuthenticated 而不检查您的 Session 变量!

            【讨论】:

            【解决方案10】:

            这对我有用

            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());
                }
            

            【讨论】:

              【解决方案11】:

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

              尝试通过我的网络应用程序上的链接以正确的方式工作。

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

              【讨论】:

                【解决方案12】:

                当我在 Web.config 中设置 authentication > forms > Path 属性时,这开始发生在我身上。删除它解决了问题,一个简单的FormsAuthentication.SignOut(); 再次删除了 cookie。

                【讨论】:

                  【解决方案13】:

                  我现在遇到了类似的问题,我相信我的问题以及原始海报的问题是由于重定向。默认情况下, Response.Redirect 会导致一个异常立即冒泡,直到它被捕获并立即执行重定向,我猜这会阻止修改后的 cookie 集合被传递给客户端。如果您修改代码以使用:

                  Response.Redirect("url", false);
                  

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

                  【讨论】:

                    【解决方案14】:

                    我只是在这里尝试了一些建议,虽然我能够使用浏览器的后退按钮,但当我单击菜单选择时,该 [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);
                            }
                    

                    虽然浏览器上的后退功能让我返回并显示安全菜单(我仍在努力),但我无法执行应用程序中的任何安全操作。

                    希望对你有帮助

                    【讨论】:

                    • 谢谢。这是对我有用的解决方案(web.config 中不需要&lt;deny users="?" /&gt;
                    【解决方案15】:

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

                    1. x64igor 给出了注销的例子:

                    • 您首先需要清除身份验证 Cookie 和会话 Cookie,方法是将 Response 中的空 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 给出了如何防止注销后缓存的示例:

                    • 您需要通过响应在客户端使缓存失效

                          // 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" ); 
                      }
                      

                    【讨论】:

                    • 浪费了一整天的时间来解决这个问题。登录后,注销按钮开始在控制器中调用错误的操作(登录而不是注销)。谢谢,这解决了问题。开发环境:ASP.NET 4.51 MVC 5.1
                    • 好答案!谦虚的建议:使用格式清除会话 cookie x64igor 使用:SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState"); HttpCookie sessionCookie = new HttpCookie(sessionStateSection.CookieName, "");。一般来说,会话​​cookie名称不是"ASP.NET_SessionId"
                    【解决方案16】:

                    请注意,如果来自 STS 的 wsignoutcleanup 消息与来自 IIS 的应用程序名称不匹配,WIF 拒绝告诉浏览器清理 cookie,我的意思是 区分大小写。 WIF 以绿色的 OK 检查作为响应,但将向浏览器发送删除 cookie 的命令。

                    所以,你需要注意你的网址的大小写敏感。

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

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

                    【讨论】:

                    • WIF? STS? ThinkTecture 身份服务器?所有这些东西是什么,它们与这个问题有什么关系?
                    • 是的,它已经过时了。但我不得不发表评论。看起来它是由机器人自动生成和发布的 :)
                    • 这些是我当时对这些系统的体验,当用户请求注销时,无论是否基于 MS 错误,它们的行为都不同。我的回答并没有针对所选的答案说些什么。它仅阐明了碰巧使用该技术的其他系统的作用。所以,没有机器人......
                    【解决方案17】:

                    我已经尝试了这个线程中的大多数答案,但没有运气。结束了这个:

                    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

                    【讨论】:

                      【解决方案18】:

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

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

                      【讨论】:

                        【解决方案19】:

                        对于 MVC,这对我有用:

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

                        【讨论】:

                          【解决方案20】:

                          经过大量搜索,这终于对我有用。希望对你有帮助。

                          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 的新手......我承认我喜欢它,但是谁能想到像注销某人这样简单的事情会如此困难?我尝试了此页面上的所有其他脚本,这是唯一有效的。感谢发帖!
                          【解决方案21】:

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

                          According to Microsoft:

                          SignOut 方法删除表单-身份验证票信息 从 cookie 或 URL 如果 CookiesSupported 为 false

                          同时they say

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

                          最后,关于 UseDeviceProfile,they say

                          如果 CookieMode 属性设置为 UseDeviceProfile,则 如果浏览器为,CookiesSupported 属性将返回 true 当前请求支持 cookie 和 cookie 重定向; 否则,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 为假,它将从 URL 中清除,否则从 cookie 中清除。 IE。他们应该写“SignOut 方法从 cookie 中删除表单身份验证票信息,或者,如果 CookiesSupported 为 false,则从 URL 中删除。”
                          【解决方案22】:

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

                          public ActionResult SignOut()
                              {
                                  if (Request.IsAuthenticated)
                                  {
                                      FormsAuthentication.SignOut();
                          
                                      return Redirect("~/");
                                  }
                                  return View();
                               }
                          

                          【讨论】:

                            【解决方案23】:

                            这个答案在技术上与 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

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2017-07-06
                              • 2019-01-05
                              • 2015-07-16
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多