Facebook SDK错误:跨站点请求伪造验证失败。必需的参数"州"持久数据中缺少

时间:2017-06-21 19:12:48

标签: php facebook facebook-graph-api facebook-php-sdk

我最近升级到facebook SDK的最新版本,我在登录用户时遇到了问题。我生成登录链接就好了,但是当facebook通过令牌将用户发回我的网站时,我得到了这个错误:

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['user_input_array@user_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);

5 个答案:

答案 0 :(得分:2)

  

fb sdk错误:跨站点请求伪造验证失败。必需的参数&#34;州&#34;持久数据中缺失。

这与你正在经历两次调用getRedirectLoginHelper和$ helper-&gt; getAccessToken()的例行程序有关 - 一次&#34;在他们自己的&#34;上,然后再次在try-catch中阻止(复制和粘贴错误,或者不幸的调试尝试?)

我现在懒得去检查SDK源代码,但我认为在代码交换代码后故意取消会话中的状态参数,这是使整个过程更加安全的一部分 - 这样当你第二次调用getAccessToken时,它会失败。

答案 1 :(得分:2)

这可能有点晚,但我希望它有助于其他人。

我有这个问题已经有一段时间了,我已经四处搜索并看到了很多不同的解决方案,其中许多都禁用了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步:在应用设置中设置重定向网址。

在您的Facebook登录应用设置中,转到有效OAuth重定向URI ,您应该在其中添加指向fb-callback.php文件的网址。

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 需要与代码中的重定向网址相匹配,如果在您的应用设置中,您只需将OAuth重定向设置为http://example.com/fb-callback.php,并将$ redirectURL设置为http://example.com/fb-bacllback.php要使其匹配,但用户以http://www.example.com进入您的网站,则用户将收到 Facebook SDK错误:跨网站请求伪造验证失败。永久数据中缺少必需的参数“状态”,因为用户所在的URL与您设置的URL不完全匹配。为什么?我没有吓人的想法。

我的方法是,如果用户以http://example.comhttp://www.example.com进入您的网站,它将始终与您在应用设置中设置的内容相匹配。为什么?因为$ _SERVER ['SERVER_NAME']将返回包含或不包含www的域名,具体取决于用户在浏览器中输入网址的方式。

这是我的发现,这是关于我唯一没有取消CSRF检查的事情,到目前为止,没有任何问题。

我希望这会有所帮助。

答案 2 :(得分:1)

如果有人仍然使用这个程序,只需在回调文件的开头添加一个 session_start()

答案 3 :(得分:0)

我知道我迟到了,但我遇到了这个错误,而且我不相信我的解决方案包含在上述回复中,所以这里是:

可能出现此错误的一个原因是,如果您多次向 Facebook 提交相同的 URL 登录请求。例如,用户可能会不耐烦地多次单击登录按钮,从而触发 URL 的多次提交。就我而言,解决方案是使用 JavaScript 函数拦截第一次点击后的所有点击。

答案 4 :(得分:-1)

点击登录后还是出现这个错误..

<块引用>

SDK 异常:跨站点请求伪造验证失败。必需的 持久数据中缺少参数“状态”。

我将只关注并阅读上一个主题和上面的评论。并仔细检查有效 OAuth 重定向 URI 的 URL。

这是 id 所做的。如果您能分享一些想法并纠正我,我们将不胜感激..

create-acc.php

///FACEBOOK SIGNUP

session_start();
include_once 'config-facebook.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();
  }

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


  $facebook_button ='

 <div style="background-color:white; color:#4b5563;    cursor:pointer;" class="inline-flex border-2 py-1.5 px-5 rounded text-lg border-gray-300">
         <div style="margin-top:5px;">
    <img style="width:25px;" src="./assets/apps/facebook-logo-2019.png"/>
 </div>
  <a href="'.$fLoginURL.'"  style="margin:5px 10px;"><b>Sign up with Facebook</b></a>
     </div>


         ';

config-facebook.php

include_once 'Facebook/autoload.php';

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


   $helper = $fb->getRedirectLoginHelper(); 

我是这个编程的新手,还在学习中。所以如果你帮助它真的节省我的时间并学习提供的代码。如果我尝试添加一些回调代码来获取数据,它在我的工作中看起来像这样

create-acc.php 附加回调代码

///FACEBOOK SIGNUP

session_start();
include_once 'config-facebook.php';

if (isset($accessToken))
 {
if (!isset($_SESSION['facebook_access_token'])) 
{
    //get short-lived access token
    $_SESSION['facebook_access_token'] = (string) $accessToken;
    
    //OAuth 2.0 client handler
    $oAuth2Client = $fb->getOAuth2Client();
    
    //Exchanges a short-lived access token for a long-lived one
    $longLivedAccessToken = $oAuth2Client->getLongLivedAccessToken($_SESSION['facebook_access_token']);
    $_SESSION['facebook_access_token'] = (string) $longLivedAccessToken;
    
    //setting default access token to be used in script
    $fb->setDefaultAccessToken($_SESSION['facebook_access_token']);
  } 
  else 
  {
    $fb->setDefaultAccessToken($_SESSION['facebook_access_token']);
}



if (isset($_GET['code'])) 
{
    header('Location: ./');
}


 try {
    $fb_response = $fb->get('/me?fields=name,first_name,last_name,email');
    $fb_response_picture = $fb->get('/me/picture?redirect=false&height=200');
    
    $fb_user = $fb_response->getGraphUser();
    $picture = $fb_response_picture->getGraphUser();
    
    $_SESSION['fb_user_id'] = $fb_user->getProperty('id');
    $_SESSION['fb_user_name'] = $fb_user->getProperty('name');
    $_SESSION['fb_user_email'] = $fb_user->getProperty('email');
    $_SESSION['fb_user_pic'] = $picture['url'];
    
    
} catch(Facebook\Exceptions\FacebookResponseException $e) {
    echo 'Facebook API Error: ' . $e->getMessage();
    session_destroy();
    
    header("Location: ./");
    exit;
} catch(Facebook\Exceptions\FacebookSDKException $e) {
    echo 'Facebook SDK Error: ' . $e->getMessage();
    exit;
}
 } 
 else 
 {  


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

     $facebook_button ='

 <div style="background-color:white; color:#4b5563;    cursor:pointer;" class="inline-flex border-2 py-1.5 px-5 rounded text-lg border-gray-300">
         <div style="margin-top:5px;">
    <img style="width:25px;" src="./assets/apps/facebook-logo-2019.png"/>
 </div>
  <a href="'.$fLoginURL.'"  style="margin:5px 10px;"><b>Sign up with Facebook</b></a>
     </div>


         ';

}

config-facebook.php

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

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


  $helper = $fb->getRedirectLoginHelper(); 

 try {
if(isset($_SESSION['facebook_access_token']))
    {$accessToken = $_SESSION['facebook_access_token'];}
else
    {$accessToken = $helper->getAccessToken();}
} catch(FacebookResponseException $e) {
 echo 'Facebook API Error: ' . $e->getMessage();
  exit;
} catch(FacebookSDKException $e) {
echo 'Facebook SDK Error: ' . $e->getMessage();
  exit;
 }

结果在这里

<块引用>

致命错误:未捕获的 Facebook\Exceptions\FacebookSDKException:跨站点请求伪造验证失败。持久数据中缺少必需的参数“状态”。在 /www/wwwroot/fruitask.com/Facebook/Helpers/FacebookRedirectLoginHelper.php:244 堆栈跟踪:#0 /www/wwwroot/fruitask.com/Facebook/Helpers/FacebookRedirectLoginHelper.php(221):Facebook\Helpers\FacebookRedirectLoginHelper- >validateCsrf() #1 /www/wwwroot/fruitask.com/config-facebook.php(20): Facebook\Helpers\FacebookRedirectLoginHelper->getAccessToken() #2 /www/wwwroot/fruitask.com/create-acc.php (162): include_once('/www/wwwroot/fr...') #3 {main} 在第 244 行的 /www/wwwroot/fruitask.com/Facebook/Helpers/FacebookRedirectLoginHelper.php 中抛出

请分享一些想法或任何其他方法,我可以使用 PHP 在我的网站上实现 Facebook 登录。提前致谢