【问题标题】:Facebook Connect & HTTP Cookies - How Can I Overcome this "Race Condition"Facebook Connect 和 HTTP Cookies - 我怎样才能克服这种“竞争条件”
【发布时间】:2025-12-29 01:05:11
【问题描述】:

如果您想知道“比赛条件”是什么,它是系统中的一个缺陷,而它高度依赖于时间。有关更多信息,请参阅 wiki here

因此,我的条件与 Facebook Connect 相关,并使用 ASP.NET 4.0 Web 应用程序(基于表单的身份验证 - IIS7)实现单点登录服务。

进程本身(包括注销)运行良好,但是.....

以下是它并非完全 100% 工作的场景:

  1. 用户登录 Facebook。
  2. 用户导航到我的网站。
  3. 用户未自动登录(应该是)。
  4. 用户刷新页面,自动登录。

当我在第 3 步中对代码进行断点时 - Facebook Cookie 还不存在(在 HttpContext.Current.Request.Cookies 中)。

但是当页面重新加载时(第 4 步)- Facebook Cookie 就在那里。

对我来说,这可能是很多事情:

我不确定这是否只是 Facebook 尚未尚未授予我的应用程序访问 cookie 的权限(跨域握手延迟 - xd_receiver.htm),还是一个问题具有跨域 cookie 本身和 ASP.NET 页面生命周期。

其他人处理过这个问题吗?

这不是“一劳永逸”,但它很烦人(从用户的角度来看也不是很好)。

编辑:

好吧,我现在注意到了一些奇怪的事情。如果我登录 Facebook(通过 Facebook),然后等待 20 秒,然后转到我的网站,它仍然没有让我登录。只有在第二次加载后它才会让我登录。所以也许这不是时间问题 - 为什么是否需要刷新 2 次才能读取 cookie?

为了消除一些混乱 - Facebook 设置 cookie(用户登录到 Facebook),我的网站会读取这些 cookie。

这发生在每个页面请求上(每个页面上的用户控件中的逻辑)

protected void Page_PreRender(object sender, EventArgs eventArgs)
{
   if (FacebookUser.IsAuthenticated) // static property, checks HttpContext.Request.Cookies
   {
        // log them into my website
   }
}

所以,在第一次刷新时 - HttpContext.Request.Cookies 中没有任何内容。

在第二次刷新时,它们就在那里。

我认为这是因为 FB.Init 在每个页面请求的客户端执行。这就是初始化 cookie 的原因。因此,当您第一次访问我的网站(登录 Facebook 后)时,在服务器端(我在其中检查 cookie),此功能尚未运行。

所以我认为我在这里打了一场失败的战斗(尝试访问 cookie 服务器端,即设置客户端)。

编辑 2:

这是我的初始化代码(在 window.load 上运行):

FB.init('myapikey', 'xd_receiver.htm', null);

我现在正在尝试做这样的事情:

FB.init('myapikey', 'xd_receiver.htm', null);
FB.getLoginStatus(function(response) {
  if (!response.session) {
      return false;
  }
  else {
    window.location.reload();
  }
});

但我收到一个 JavaScript 错误 - “FB.getLoginStatus”不是一个函数。 =(

这是因为我使用了以下 JavaScript 库: http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US

而其他人说要使用这个: http://connect.facebook.net/en_US/all.js

我正在查看新 JS API 的文档。

为了参考其他偶然发现这个线程的人,这里是“旧”JS API 的文档: http://developers.facebook.com/docs/reference/oldjavascript/

所以我已经通过使用 FB.Connect.get_status() 解决了这个问题,如果用户通过了身份验证,则重新加载窗口。

感谢大家的帮助。

如果其他人关心,这是我对问题的“解决方案”:

window.onload = function() { 
                FB.init('{0}', 'xd_receiver.htm');
                FB.ensureInit(function() {
                   FB.Connect.ifUserConnected(onUserConnected, onUserNotConnected); 
                });
            };

            function onUserConnected() {
                alert('connected!');
                window.location.reload();
            }

            function onUserNotConnected() {
                alert('not connected');
            }

当然,您应该在执行 window.location.reload() 之前检查 Forms Authentication cookie,否则页面将一直刷新。 =)

【问题讨论】:

  • 比赛条件...有一个标签 ;)

标签: asp.net cookies facebook cross-domain race-condition


【解决方案1】:

我遇到了同样的问题。页面重新加载起作用的原因是因为它们导致 FB.init() 再次被调用(我怀疑这是实际设置 cookie 的函数)。

因此,您无需重新加载页面即可实现相同的效果。 这是我所写内容的程式化版本。我不知道这是否是“正确”的方式,但它确实有效......

FB.getLoginStatus(function(response) {
    if (response.session) {
       window.location.href = '/connect/cb/fb'; 
    }
    else{
       FB.login( function(resp){FBResponse(resp);},perms);
    }
});

var FBResponse = 函数(响应,停止){ 如果(响应。会话){ window.location.href = '/connect/cb/fb'; } 别的{ 如果(停止)返回; FB.init({appId:FBappId, status:true, cookie:true, xfbml:true}); FB.getLoginStatus(function(resp){FBResponse(resp,'stop');}); }; }

【讨论】:

  • 这真的不是 Facebook 问题,而是表单身份验证和 HTTP 请求/响应生命周期的本质。 FB.Init 在客户端被调用(在页面被渲染之后)。在这一点上,我们确实知道用户已通过身份验证 - cookie 已创建(如您所说)。但是该页面已经加载(作为未经身份验证的用户)。通过刷新页面,您可以通过 HTTP 请求传递这些“新”cookie,从而启用单点登录。这是您可以克服这个问题的唯一方法 - 但是我已经更改了我的代码,而不是进行完全刷新,而是进行 ajax 更新。
【解决方案2】:

我也有同样的问题。你能找出原因吗?令人惊讶的是,它适用于我的生产服务器,但不适用于 localhost(由于 localhost 相关问题,我已将 /etc/host 更改为将 localhost 称为 peta.edu)

多次刷新对我也不起作用。但是,如果我手动单击 URL 栏并按 Enter,它就可以正常工作。

问候,

尼提恩。

【讨论】:

  • 正如我上面所说,只有在您进行 FB.Init 调用时,cookie 才会在您的网站上收到 - 这是在客户端(在页面呈现后)。因此,您不能“在服务器上获取客户端 cookie”,因为尚未创建 cookie。只需使用一些 ajax 智能来刷新您关心的页面部分。
【解决方案3】:

这不是“竞争条件”,可以更好地描述为基于浏览器的身份验证工作方式的副作用。这里涉及三个“演员”:

  1. 您的申请
  2. 脸书
  3. 用户

当用户访问您的应用程序时,Facebook 尚未参与该过程。这意味着您不知道用户是否是已经授权您的应用程序的 Facebook 用户,因为 Facebook 还没有机会告诉您。这就是为什么您在第一个请求中没有收到任何 cookie。

现在,当您的应用程序响应此请求时,它会在其中包含 Facebook JavaScript。 JavaScript SDK 将使用 IFrame(if you tell it to using the status: true optionmore explicitly)在后台 ping Facebook,并根据当前登录 Facebook 的用户(如果有)从它那里获得响应。此时,如果返回了一个会话(即有一个登录的用户过去曾授权过你的应用程序),Facebook 的JavaScript SDK 将设置一个cookie(if you tell it to do so using the cookie: true option)。

这就是为什么第一个请求没有获得 cookie 背后的逻辑。除了设置 Cookie 之外,您还可以通过 JavaScript Events 收到通知,这将允许您采取某些行动。最常见的操作是简单地重新加载页面并允许服务器注意到新设置的 cookie 并呈现适当的登录视图。但另一种选择是使用 Ajax 并做一些不涉及重新加载整个页面的更有趣的事情。

至于为什么它只在第二页面加载时发生,我不确定。但这就是它的设计工作方式:)

注意:我没有进入 advanced 使用场景,它涉及一个完整的页面重定向到提供 错觉解决这个问题。我强烈推荐使用 JavaScript SDK,因为它以很小的可用性成本提供了一个更简单且通常性能更高的解决方案。但如果你足够关心,我也可以详细说明。

【讨论】:

  • 我还是 FBC 的新手,但是“自动化回调”是什么意思?用户登录 Facebook,然后访问我的网站。我的网站检查每个页面请求上的 cookie 以查看它们是否经过身份验证。没有 ajax,它发生在 Page_Load...如果 cookie 存在,请使用我的成员模式检查它们,使用 Forms Auth 登录它们。问题是如果他们在登录后直接进入我的网站,cookies 就不在那里。如果他们说等待 5 秒,一切都很好。我是否以错误的方式进行自动登录?
  • 也许我不明白你是怎么做的。您是否使用 JavaScript SDK 进行授权?也就是说,谁设置了您的应用程序正在读取的 cookie?
  • 在这种情况下,Facebook 设置 cookie(因为我通过 Facebook 登录)。然后我只是在我的应用程序(httpcontext.request.cookies)中读取这些 cookie。我只将 JS SDK 用于两件事 - 注册跨域接收器文件 (FB.init - xd_receiver) 和注销 (FB.Connect.logout)。我还注意到这种“竞争条件”有些奇怪——我正在更新我的问题。
  • Facebook 无法直接在您的域上设置 cookie,这是 JS SDK 执行此操作的。您可能会看到对您的应用程序的第一个请求没有 cookie——因为 JS SDK 还没有机会运行。我会编辑我的答案,因为我认为我没有足够的空间来解释:)
  • 感谢您的帮助。一句话—​​—FB很烂。 =)。这么多的 doco、api、.net 端口,他们每天都在改变。
【解决方案4】:

由于您无法控制竞争条件的入口点(用户登录到 facebook),因此您可以执行的选项数量有限:

  1. 在第 3 步添加延迟(也许几毫秒的睡眠将使 Facebook 有足够的时间来登录您的使用)。这将产生部分修复的不良影响:它将适用于“大多数”情况,并且会导致您的所有登录出现延迟。

  2. 如果验证在第 3 步失败,则等待几毫秒(测试以找到正确的数量),然后再次尝试验证。然后才显示未认证的页面。

  3. 如果这是一个页面生命周期问题(不太可能),请为您的登录页面添加一个额外的页面/重定向。这意味着您的流程将如下所示:

    a) 用户登录 facebook
    b) 用户导航到您的网站(尚未设置 cookie)
    c) 用户被重定向到登录页面(可能设置了 cookie)。如果用户尚未通过身份验证,请等待 100 毫秒,然后重定向到步骤 b)(仅一次)。
    d) 用户已登录

【讨论】:

  • 问题是,我检查每个页面请求的cookies。因此,如果我添加延迟,这将导致每个页面都被延迟。不理想。不过感谢您的回答。请记住,当我说我“自动登录”时,我没有将它们重定向到任何页面,当它们刷新我网站上的任何页面时,它会检查 cookie 并将它们登录。
  • 我已经更新了我的问题。似乎与时间无关,我的网站出于某种原因需要 2 次刷新才能注册 cookie。