2017-06-21 135 views
0

我最近升级到了最新版本的facebook SDK,并且遇到登录用户的问题。我生成登录链接就好了,但是当facebook将用户发回我的站点时与令牌,我得到此错误:Facebook SDK错误:跨站请求伪造验证失败。持久数据中缺少必需的参数“状态”

fb sdk错误:跨站请求伪造验证失败。持久数据中缺少必需的参数“状态”。

我试图做一些故障排除。我打印出会话数据中的所有内容以及GET请求中的所有内容。我看到GET有一个状态参数,会话数据有一个FBRLH_state参数。它们都具有相同的价值。那么它如何告诉我该参数缺失?

我试过一些我在其他问题上看到的建议(即开始会话),但似乎没有任何工作。

任何帮助将不胜感激!我正在使用php-graph-sdk-5.5。我的Facebook连接文件低于

if(!class_exists('facebook')){ 
    class facebook{ 

     private $db = null; 
     private $fb = null; 
     private $token = null; 
     private $DEV = null; 
     private $sdk_error = null; 
     private $api_error = null; 
     private $verbose = false; 
     private $graph_user = null; 
     private $db_helper = null; 
     private $errors = null; 

     public function __construct($db, 
            $fb_id = FB_APP_ID, 
            $fb_secret = FB_APP_SECRET, 
            $fb_version = FB_DEFAULT_GRAPH_VERSION){ 
      if($this->verbose) echo '<pre>'; 
      if($this->verbose) echo 'starting construction'.PHP_EOL; 
      $this->db = $db; 
      if(!$this->fb){ 
       $this->log[] = 'no connect found. building..'.PHP_EOL; 

       $this->fb = new Facebook\Facebook(array(
          'app_id' => $fb_id, 
          'app_secret' => $fb_secret, 

          'default_graph_version' => $fb_version)); 
       if(!$this->fb){ 
        die('facebook initialization failure'); 
       } 
       $this->log[] = 'finished building new connection'.PHP_EOL; 
      } 
     } 

     public function get_login_url($callback_uri, $permissions = ['email','user_birthday']){ 

      global $_DEV,$_config; 
      $helper = $this->fb->getRedirectLoginHelper(); 

      $callback_host = ($_DEV ? $_config['dev_domain'] : $_config['live_domain']); 
      $callback_url = 'https://'.$callback_host.$callback_uri; 
      return $helper->getLoginUrl($callback_url, $permissions); 
     } 

     public function catch_token(){ 
      if($this->token){ 
       $this->log[] = 'already have token.'.PHP_EOL; 

       return $this->token; 
      } else if(!$this->fb){ 
       $this->log[] = $this->error[] = 'no facebook connection in catch token()'; 

      } 

      $this->log[] = 'starting catch token routine.'.PHP_EOL; 
      //$_SESSION['state']=$_GET['state']; 
      echo '<pre>' . var_export($_SESSION, true) . '</pre>'; 
         echo '<BR><BR><pre>' . var_export($_GET, true) . '</pre>'; 
       $helper = $this->fb->getRedirectLoginHelper(); 

       $this->token = $helper->getAccessToken(); 

       $this->log[] = 'caught token: '.$this->token; 
       $string_token = $this->token.PHP_EOL; 
       //die($string_token); 
      try { 

       $helper = $this->fb->getRedirectLoginHelper(); 

       $this->token = $helper->getAccessToken(); 

       $this->log[] = 'caught token: '.$this->token; 
       $string_token = $this->token.PHP_EOL; 

       return $this->user_flush(); 
      } catch(Facebook\Exceptions\FacebookResponseException $e) { 
       // When Graph returns an error 
       $this->log[] = $this->errors[] = 'fb api error: ' . $e->getMessage(); 
       return null; 
      } catch(Facebook\Exceptions\FacebookSDKException $e) { 
       // When validation fails or other local issues 
       $this->log[] = $this->errors[] = 'fb sdk error: ' . $e->getMessage(); 
       return null; 
      } catch(Exception $e){ 
       $this->log[] = $this->errors[] = 'unknown error: '.$e->getMessage(); 
       return null; 
      } 
     } 

     public function get_token(){ 
      $this->log[] = 'get token called.'.PHP_EOL; 
      if($this->token){ 
       $this->log[] = 'token found in object'.PHP_EOL; 
       //echo '<pre>'; 
       //die(debug_print_backtrace()); 
       return $this->token; 
      } else { 
       $this->log[] = $this->errors[] = 'token not found in object.'.PHP_EOL; 
       return null; 
      } 
     } 

     public function get_user($override = false){ 
      $fields = array(
       'first_name', 
       'last_name', 
       'email', 
       'id', 
       'picture', 
       'birthday', 
       'gender',); 
      $fields = implode(',',$fields); 
      if($this->graph_user === null){ 
       if($this->fb && $this->get_token()){ 
        try { 
         // Returns a Facebook\FacebookResponse object 
         $resp_url = '/me?fields='.$fields.'&debug=all'; 
         $this->log[] = $resp_url; 
         $response = $this->fb->get($resp_url, $this->get_token()); 
         $this->graph_user = $response->getGraphUser(); 
         return $this->graph_user; 
        } 
        catch(Facebook\Exceptions\FacebookResponseException $e) { 
         // When Graph returns an error 
         $this->api_error = 'fb api error: ' . $e->getMessage(); 
         $this->errors[] = $this->api_error; 
         return null; 
        } 
        catch(Facebook\Exceptions\FacebookSDKException $e) { 
         // When validation fails or other local issues 
         $this->sdk_error = 'fb sdk error: ' . $e->getMessage(); 
         $this->errors[] = $this->sdk_error; 
         return null; 
        } 
       } else { 
        $this->sdk_error = "get_user(): fb connection or token not set. are you logged in?"; 
        $this->errors[] = $this->sdk_error; 
        //echo '<pre>'; 
        //debug_print_backtrace(); 
        //die('token: '.$this->token); 
        return null; 
       } 
      } else { 
       $this->sdk_error = "get_user(): graph_user already set"; 
       $this->errors[] = $this->sdk_error; 
       return $this->graph_user; 
      } 

     } 

     public function get_user_first_name(){ 
      return $this->get_user()['first_name']; 
     } 
     public function get_user_last_name(){ 
      return $this->get_user()['last_name']; 
     } 
     public function get_user_id(){ 
      return $this->get_user()['id']; 
     } 
     public function get_user_email(){ 
      return $this->get_user()['email']; 
     } 
     public function get_user_picture(){ 
      return $this->get_user()['picture']['url']; 
     } 
     public function get_user_birthday(){ 
      return $this->get_user()['birthday']; 
     } 

     public function user_flush(){ 
      //this is the command function. 
      // runs the basic functionality of this class 
      // by adding this user to the database if they're not there 
      //  and logging them in if they are. 
      $this->graph_user = $this->get_user(); 
      //$this->log['graph_user_at_user_flush'] = $this->graph_user; 
      $this->build_user(); 
      $this->log['GRAPH_USER'] = $this->get_user(); 
      $this->log['[email protected]_flush'] = $this->user_input; 
      if($return = $this->user->fb_register()){ 
       //die(print_r(debug_backtrace(),true)); 
       //$this->log['success return'] = '. '.$return; 
       return $return; 
      } else { 
       //die('<pre>'.print_r(debug_backtrace(),true)); 
       $this->log['fb_register_fail'] = array('fb_register() (also login) failed.',$this->user->get_errors()); 
       return null; 
      } 
     } 

     public function build_user(){ 

      $this->user_input['first_name'] = $this->get_user_first_name(); 
      //$this->user_input['last_name'] = $this->get_user_last_name(); 
      $this->user_input['facebook_id'] = $this->get_user_id(); 
      $this->user_input['email'] = $this->get_user_email(); 
      $this->user_input['image_url'] = $this->get_user_picture(); 
      $this->user_input['birthday'] = $this->get_user_birthday(); 
      if($this->verbose) 
       print_r($this->user_input); 
      $this->user = new user($this->user_input,$this->db); 
     } 

     public function logout(){ 
      unset($_SESSION['fb_id']); 
      unset($this->token); 
      unset($this->fb); 
     } 

     public function get_errors(){ 
      return array_unique($this->errors); 
     } 
     public function get_log(){ 
      return array_unique($this->log); 
     } 
    } 
} 


//finally, create the connection. 
if(!isset($fb)) 
    $fb = new facebook($db); 
+0

我想这可能与你正在经历getRedirectLoginHelper和$ helper-> getAccessToken()的例程两次有一些奇怪的原因... – CBroe

+0

@CBroe谢谢!那就是诀窍。如果您将其作为答案提交,我会将其标记为正确。 – user2874270

回答

1

fb sdk error: Cross-site request forgery validation failed. Required param "state" missing from persistent data.

它是与你通过日常会调用getRedirectLoginHelper和$ helper-> getAccessToken()两次 - 一次“靠自己”,和然后再次在一个try-catch块(复制&粘贴错误,或不幸的调试尝试可能?)

我有点懒得去检查SDK源,但我认为它故意取消设置状态参数在代码交换为令牌之后的会话内部,作为使整个过程更安全的一部分 - 以便在调用getA时ccessToken第二次,它失败。

+0

我必须得到相同的错误,并且不要像“getAccessToken”一样检查重复的代码。你能帮我么。我的演示网址是http://demo.sigzen.net/fb-login/v5/ –

0

这可能有点晚,但我希望它可以帮助别人。

我有这个问题一段时间,我搜索了很多不同的解决方案,其中许多都禁用了CSRF检查。所以在我读过的所有东西之后,这对我来说是有效的。

据我所知,当您的重定向网址与您在应用设置中设置的网址不匹配时出现此错误,所以我的问题每次都很容易修复,但我也看到人们因没有会话而出现问题开始正确,所以我会涵盖这两个问题。

第1步:确保您的会话在需要时开始。

例如:FB-config.php文件

session_start(); 
include_once 'path/to/Facebook/autoload.php'; 

$fb = new \Facebook\Facebook([ 
    'app_id' => 'your_app_id', 
    'app_secret' => 'your_secret_app_id', 
    'default_graph_version' => 'v2.10' 
]); 

$helper = $fb->getRedirectLoginHelper(); 

如果你的Facebook回调代码是从配置其他文件放在一边,然后开始在该文件中的会话了。

例如:FB-callback.php

session_start(); 
include_once 'path/to/fb-config.php'; 

try { 
    $accessToken = $helper->getAccessToken(); 
} catch (\Facebook\Exceptions\FacebookResponseException $e) { 
    echo "Response Exception: " . $e->getMessage(); 
    exit(); 
} catch (\Facebook\Exceptions\FacebookSDKException $e) { 
    echo "SDK Exception: " . $e->getMessage(); 
    exit(); 
} 

/** THE REST OF YOUR CALLBACK CODE **/ 

现在,解决我的实际问题。

第3步:在您的应用程序设置中设置您的重定向URL。

在你的Facebook登录的应用程序设置,转到有效的OAuth重定向,你应该已经添加指向你的FB-callback.php文件的URL的URI

http://example.com/fb-callback.php 

AND ALSO 

http://www.example.com/fb-callback.php 

然后设置您的重定向网址如下。

$redirectURL = "http://".$_SERVER['SERVER_NAME']."/fb-callback.php"; 
$permissions = ['email']; 
$fLoginURL = $helper->getLoginUrl($redirectURL, $permissions); 

为什么无论有没有www和为什么使用SERVER_NAME?

因为你有效的OAuth重定向URI需要在你的代码符合您重定向URL,如果在你的应用程序设置,只设置了您的OAuth重定向为http://example.com/fb-callback.php并设置$的redirectUrl为http://example.com/fb-bacllback.php使其匹配,但用户输入您的网站作为http://www.example.com然后用户将得到Facebook SDK错误:跨站请求伪造验证失败。持久数据中缺少必需的参数“状态”,因为用户所在的URL不完全符合您的设置。为什么?我没有任何想法。

我的方法使得如果用户以http://example.comhttp://www.example.com的身份输入您的网站,它将始终与您在应用设置中设置的内容相匹配。为什么?因为根据用户在浏览器中输入url的方式,$ _SERVER ['SERVER_NAME']将返回包含或不包含www的域。

这是我的发现,这是关于唯一的工作,我没有删除CSRF检查,迄今没有问题。

我希望这会有所帮助。

相关问题