Facebook Connect& HTTP Cookie - 我如何克服这种“竞争条件”

时间:2010-07-19 03:49:03

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

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

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

流程本身(包括Logout)运行良好,但是......

这是一个不完全正常工作的情况:

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

当我在第3步中断开代码时 - Facebook Cookie尚未出现(在HttpContext.Current.Request.Cookies中)。

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

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

我不确定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的doco。

对于偶然发现这个线程的其他人的参考,这里是'旧'JS API的doco: 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身份验证cookie,否则页面将继续刷新。 =)

4 个答案:

答案 0 :(得分:1)

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

  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并做一些不需要重新加载整个页面的更好的东西。

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

    注意:我没有进入高级使用方案,该方案涉及整页重定向到未记录但受支持的端点(JavaScript SDK使用的相同端点),它提供了错觉修复此问题。我强烈建议使用JavaScript SDK,因为它以较小的可用性成本提供了更简单且通常性能更高的解决方案。但如果你足够关心我也可以详细说明。

答案 1 :(得分:0)

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

  1. 在步骤nr 3添加延迟(也许几毫秒的睡眠会让Facebook有足够的时间来签署您的使用)。这将产生部分修复的恶劣效果:它将适用于“大多数”情况,并且会导致所有登录延迟。

  2. 如果在步骤3中验证失败,那么等待几毫秒(测试找到正确的ammount),然后再次进行验证。然后才显示未经过身份验证的页面。

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

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

答案 2 :(得分:0)

我有完全相同的问题。你能弄明白这个的原因吗?令人惊讶的是,它适用于我的生产服务器,但不适用于localhost(由于localhost相关问题,我已将/ etc / host更改为将localhost引用为peta.edu)

多次刷新对我来说也不起作用。但是,如果我手动点击URL栏并按Enter键,它就可以了。

此致

尼西。

答案 3 :(得分:0)

我遇到了同样的问题。页面重新加载的原因是因为它们会再次调用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 = function(response,stop){ if (response.session){ window.location.href = '/connect/cb/fb'; } else{ if (stop) return; FB.init({appId:FBappId, status:true, cookie:true, xfbml:true}); FB.getLoginStatus(function(resp){FBResponse(resp,'stop');}); }; }