Facebook PHP SDK 5 :: API 2.4 ::跨站点请求伪造验证失败。必需的参数“状态”缺失

时间:2015-07-20 15:33:33

标签: php facebook facebook-php-sdk facebook-login facebook-access-token

我做了一个非常简单的PHP脚本,只是尝试通过Facebook登录并获得accessToken。但是,当我尝试以下代码时,我从SDK中获得了一个例外:«跨站点请求伪造验证失败。必需的参数“状态”缺失。 »。

这是我的代码:

require_once __DIR__ . '/facebook-sdk-v5/autoload.php';
session_start();

$fb = new Facebook\Facebook([
    'app_id' => '{my-own-app-id}',
    'app_secret' => '{my-own-app-secret}'
]);

// Check to see if we already have an accessToken ?
if (isset($_SESSION['facebook_access_token'] )) {
    $accessToken = $_SESSION['facebook_access_token'];
    echo "Horray we have our accessToken:$accessToken<br />\n";

} else {
    // We don't have the accessToken
    // But are we in the process of getting it ? 
    if (isset($_REQUEST['code'])) {
        $helper = $fb->getRedirectLoginHelper();
        try {
            $accessToken = $helper->getAccessToken();
            } catch(Facebook\Exceptions\FacebookResponseException $e) {
              // When Graph returns an error
              echo 'Graph returned an error: ' . $e->getMessage();
              exit;
        } catch(Facebook\Exceptions\FacebookSDKException $e) {
              // When validation fails or other local issues
              echo 'Facebook SDK returned an error: ' . $e->getMessage();
            exit;
        }

        if (isset($accessToken)) {
              // Logged in!
              $_SESSION['facebook_access_token'] = (string) $accessToken;

              // Now you can redirect to another page and use the
              // access token from $_SESSION['facebook_access_token']

              echo "Finally logged in! Token:$accessToken";
        }           
    } else {
        // Well looks like we are a fresh dude, login to Facebook!
        $helper = $fb->getRedirectLoginHelper();
        $permissions = ['email', 'user_likes']; // optional
        $loginUrl = $helper->getLoginUrl('http://mywebsite.com/myapp/index.php', $permissions);

        echo '<a href="' . $loginUrl . '">Log in with Facebook!</a>';
    }

}

exit;

23 个答案:

答案 0 :(得分:23)

我必须在某些服务器中添加这些行:

$helper = $fb->getRedirectLoginHelper();
if (isset($_GET['state'])) {
    $helper->getPersistentDataHandler()->set('state', $_GET['state']);
}

我随机收到此错误,具体取决于服务器配置。

答案 1 :(得分:9)

如果您使用网站的“www”版本生成登录链接,并且您被重定向到非www版本,则会遇到会话问题。因此,请确保您访问您网站的www版本,然后将回调网址定义为相同的www版本。此外,您可以在服务器配置中定义永久重定向,以确保从非www版本访问您网站的任何人都被重定向到www版本,反之亦然。

答案 2 :(得分:9)

两个脚本开头的

session_start()。我从这里得到了解决方案:https://github.com/facebook/facebook-php-sdk-v4/issues/473

答案 3 :(得分:3)

在研究stackoverflow之后,我也遇到了同样的问题 放线

$_SESSION['FBRLH_state']=$_GET['state']; 

以上解决了我的问题

$helper = $fb->getRedirectLoginHelper();  

答案 4 :(得分:2)

如果源主机名与经过身份验证的目标主机名不同,则会收到此错误。

$loginUrl = $helper->getLoginUrl('http://mywebsite.com/myapp/index.php', $permissions);

使用此声明,如果您网站上的访问者使用http://www.mywebsite.com/,则会引发跨网站错误。

您必须确保源和目标主机名完全相同,包括最终的www前缀。

修正版:

$loginUrl = $helper->getLoginUrl('http://'.$_SERVER['SERVER_NAME'].'/myapp/index.php', $permissions);

答案 5 :(得分:2)

这是很多人在FB Api面临的常见问题。这只是一个SESSION问题。要解决此问题,请添加一些代码,例如。

在回调脚本上通常fb-callback.php add&#34; session_start();&#34;就在你加入facebook自动加载文件之前。然后&#34; $ _ SESSION [&#39; FBRLH_state&#39;] = $ _ GET [&#39; state&#39;];&#34; &#34; $ helper = $ fb-&gt; getRedirectLoginHelper();&#34;线。

示例:

<?php 
session_start(); /*Add session start*/
include 'vendor/autoload.php';
include 'config.php'; /*Facebook Config*/
$helper = $fb->getRedirectLoginHelper();
$_SESSION['FBRLH_state']=$_GET['state']; /*Add This*/
try {
  $accessToken = $helper->getAccessToken();
} ?>

答案 6 :(得分:2)

在获得令牌之前明确设置[PersistentDataHandler]将保证请求成功。下面显示了如何获取$ _GET [&#39; state&#39;]&amp;只需将其注入&#34;即可使用[帮助者]&#34;在Symfony 2.x上3.X

我有与O.P.完全相同的问题,这解决了我的问题。

//create your new facebook sdk instance
$this->fb = new Facebook([
   'app_id' => $facebookAppId,
   'app_secret' =>$facebookAppSecret,
   'default_graph_version' =>$facebookDefaultGraphVersion
]);    

//retrieve the helper
$this->helper = $this->fb->getRedirectLoginHelper();

//below is the money shot
//setting this explicitly before you get your tokens will guarantee that the request will be a success. It Fetches $_GET['state'] & simply injects it into the "to be used [helper]"
$this->helper->getPersistentDataHandler()->set('state', $request->query->get('state'));

答案 7 :(得分:1)

对我来说,问题现在只需通过添加以下内容来启动会话即可解决:

session_start();

在两个文件的开头(第一个文件生成facebook url和回调文件:login.php和fb-callback.php(https://developers.facebook.com/docs/php/howto/example_facebook_login))。

我还必须补充一下:

$config['app_id'] = 'myapp_id';

位于顶部,以防止出现其他非相关错误。

答案 8 :(得分:1)

答案 9 :(得分:1)

我试图用Facebook PHP SDK在Symfony中实现Facebook登录,我遇到了同样的错误&#34;跨站点请求伪造验证失败。必需的参数“状态”缺失&#34;

我通过使用自定义处理程序将 persistent_data_handler 参数添加到我的facebook app instanciation来解决了这个问题,该处理程序实现了Facebook PHP SDK的PersistentDataInterface。

像魅力一样。

public function facebookCallbackAction(Request $request) {
    $session = $request->getSession();

    $fb = new \Facebook\Facebook([
        'app_id' => $this->container->getParameter('facebook_app_id'),
        'app_secret' => $this->container->getParameter('facebook_app_secret'),
        'default_graph_version' => 'v2.5',
        'persistent_data_handler' => new SymfonyPersistentDataHandler($session),
    ]);
}

我的自定义处理程序:

use Facebook\PersistentData\PersistentDataInterface;
use Symfony\Component\HttpFoundation\Session\Session;

class SymfonyPersistentDataHandler implements PersistentDataInterface {
    protected $session;
    protected $sessionPrefix = 'FBRLH_';

    public function __construct(Session $session) {
        $this->session = $session;
    }

    public function get($key) {
        return $this->session->get($this->sessionPrefix . $key);
    }

    public function set($key, $value) {
        $this->session->set($this->sessionPrefix . $key, $value);
    }
}

答案 10 :(得分:1)

我遇到了类似的问题,发现了Nanang Koesharwanto的解决方案。它更像是quirk,因为在供应商目录中修改源文件是一个非常坏主意。 所以这就是诀窍。

public function callback(Request $request)
{
    $this->helper->getPersistentDataHandler()->set('state', $request->state);
    return $this->helper->getAccessToken();
}

如果失败,请将use Session;放入控制器。

答案 11 :(得分:0)

我通过在Facebook开发者帐户的“ Facebook->登录->有效OAuth重定向URI”中设置回叫网址来解决此错误。

答案 12 :(得分:0)

这里真正的问题是我的PHP文件的编码。我使用的是UTF8,但它应该是没有BOM的UTF8。

错误编码的副作用是我的SESSION无法正常工作,然后SDK无法检索必要的信息才能正常工作。

正确配置的错误报告会告诉我直接存在问题。

我认为我们可以填补&#34; noob&#34;类别。但是,我认为它对我这样的其他新手也很有用。

答案 13 :(得分:0)

如果session_start()仍不能解决问题,则您在Facebook开发人员应用的有效OAuth重定向URI中输入了错误的URI。

解决: 首先,转到fb-callback.php,找到您放入标头函数中的URI,例如:header(“ Location:https://localhost/home.php”)。然后转到您的Facebook开发人员应用程序,然后在边栏中单击“产品”选项卡下的Facebook登录,然后单击“设置”。在有效的OAuth重定向URI中,添加您放入标头中的URI。从我的示例中,我将放https://localhost/home.php

希望这会有所帮助。

答案 14 :(得分:0)

对我来说,解决方案是通过将命名空间Facebook\Exceptions\FacebookSDKException替换为Exception来捕获异常,因为脚本已经使用过它。

use Facebook\Facebook;

// code ...

$fb = new Facebook([
    'app_id' => FB_APP_ID,
    'app_secret' => FB_APP_SECRET,
    'default_graph_version' => 'v2.5',
]);

$helper = $fb->getRedirectLoginHelper();
try {
    $accessToken = $helper->getAccessToken();
} catch (Exception $e) {
    echo $e->getMessage();
    exit;
}

答案 15 :(得分:0)

我的修复方法是在config / session.php中将'secure' => true更改为false。 我在第一时间没有使用https时意外地将其设置为true。

答案 16 :(得分:0)

对我来说,由于&#39; persistent_data_handler&#39; ,这种情况正在发生。通过在Facebook配置中添加它,我能够使它工作。

session_start();
$fb = new Facebook\Facebook([
  'app_id'     => '6XXXXXXXXX',
  'app_secret' => '5XXXXXXXXXXXXXX',
  'default_graph_version' => 'v2.6', 
  'persistent_data_handler' => 'session'
  ]);

答案 17 :(得分:0)

对于那些使用cakephp 3.x并且遇到此问题并且您不知道如何解决它的人。 添加session_start();在您的身份验证和回调方法的开头。

public function Facebookauth()
{
session_start();
 $fb = new Facebook([
      'app_id' => '{app_id}',
      'app_secret' => '{app_secret}',
      'default_graph_version' => 'v2.6',
     ..........

    ]);

you can still use 
     $session = $this->request->session();

答案 18 :(得分:0)

Laravel 5.2

我也有这个错误“跨站点请求伪造验证失败。必需的参数”状态“丢失”。

并在阅读this几个小时之后。我试图更改供应商脚本。

vendor\facebook\php-sdk-v4\src\Facebook\Helpers\FacebookRedirectLoginHelper.php中的

在第123行,我改变了这个脚本:

private function makeUrl($redirectUrl, array $scope, array $params = [], $separator = '&')
{
    $state = $this->pseudoRandomStringGenerator->getPseudoRandomString(static::CSRF_LENGTH);
    $this->persistentDataHandler->set('state', $state);

    return $this->oAuth2Client->getAuthorizationUrl($redirectUrl, $state, $scope, $params, $separator);
}

进入(我添加Session::put('state', $state);

private function makeUrl($redirectUrl, array $scope, array $params = [], $separator = '&')
{
    $state = $this->pseudoRandomStringGenerator->getPseudoRandomString(static::CSRF_LENGTH);
    $this->persistentDataHandler->set('state', $state);
    Session::put('state', $state);
    return $this->oAuth2Client->getAuthorizationUrl($redirectUrl, $state, $scope, $params, $separator);
}

在第234行,我改变了这个脚本:

protected function validateCsrf()
{
    $state = $this->getState();
    $savedState = $this->persistentDataHandler->get('state');

    if (!$state || !$savedState) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. Required param "state" missing.');
    }

    $savedLen = strlen($savedState);
    $givenLen = strlen($state);

    if ($savedLen !== $givenLen) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }

    $result = 0;
    for ($i = 0; $i < $savedLen; $i++) {
        $result |= ord($state[$i]) ^ ord($savedState[$i]);
    }

    if ($result !== 0) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }
}

进入(我添加了$this->persistentDataHandler->set('state', Session::get('state'));

protected function validateCsrf()
{
    $state = $this->getState();
    $this->persistentDataHandler->set('state', Session::get('state'));
    $savedState = $this->persistentDataHandler->get('state');

    if (!$state || !$savedState) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. Required param "state" missing.');
    }

    $savedLen = strlen($savedState);
    $givenLen = strlen($state);

    if ($savedLen !== $givenLen) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }

    $result = 0;
    for ($i = 0; $i < $savedLen; $i++) {
        $result |= ord($state[$i]) ^ ord($savedState[$i]);
    }

    if ($result !== 0) {
        throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
    }
}

这就是我所做的一切。并且错误消失了。

答案 19 :(得分:0)

您必须确保会话在脚本运行之前启动。但如果再次在同一个脚本上启动会话,它将再次抛出443: Network is unreachable。希望这对某人有所帮助。

我刚刚用过 if (session_status() == PHP_SESSION_NONE){ session_start(); }

答案 20 :(得分:0)

最后,看一下FB代码,我发现问题“跨站点请求伪造验证失败。必需的参数”状态“缺失”和类似物是由PHP变量$ _SESSION ['FBRLH_state']引起的,对于某些“奇怪” “当FB调用login-callback文件时的原因。

为了解决这个问题,我在调用函数$ helper-&gt; getLoginUrl(...)之后存储了这个变量“FBRLH_state”。只有在调用此函数后才能执行此操作,因为当填充变量$ _SESSION ['FBRLH_state']时,此函数位于此函数内。

下面是我在login.php中的代码示例:

$uri=$helper->getLoginUrl($uri, $permissions);
foreach ($_SESSION as $k=>$v) {                    
    if(strpos($k, "FBRLH_")!==FALSE) {
        if(!setcookie($k, $v)) {
            //what??
        } else {
            $_COOKIE[$k]=$v;
        }
    }
}
var_dump($_COOKIE);

在调用所有FB代码之前的login-callback.php中:

foreach ($_COOKIE as $k=>$v) {
    if(strpos($k, "FBRLH_")!==FALSE) {
        $_SESSION[$k]=$v;
    }
}

最后,但并非最不重要的是,还要记住包含PHP会话的代码,以便......

if(!session_id()) {
    session_start();
}
...
...
...
...
<?php session_write_close() ?>

我希望这个回复可以帮助你节省8-10个小时的工作:) 再见,亚历克斯。

答案 21 :(得分:-1)

无论你做什么。只是不要多次调用getAccessToken()。它会在调用后立即从会话中删除状态。

答案 22 :(得分:-1)

最后在所有这些不错的错误之后, 我用另一个解决方案解决了所有问题, 来自facebook开发者文档的示例已过时。

SDK V5和APi 2.4 与[{3}} 中描述的一样有效 需要定义访问令牌

请务必使用您的凭据填写 APP-IP | APP-SECRET。

教程示例:

$fb = new Facebook\Facebook([
      'app_id' => 'APP-ID',
      'app_secret' => 'APP-SECRET',
      'default_graph_version' => 'v2.4',
      'default_access_token' => 'APP-ID|APP-SECRET'
]);

使用此而不是开发人员facebook docs上的示例。

玩得开心:)