php Facebook SDK 返回错误:跨站点请求伪造验证失败。URL 和会话中的“状态”参数不匹配
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32029116/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Facebook SDK returned an error: Cross-site request forgery validation failed. The "state" param from the URL and session do not match
提问by Fadi
i'm trying to get Facebook user id using the php sdk like this
我正在尝试使用这样的 php sdk 获取 Facebook 用户 ID
$fb = new Facebook\Facebook([
'app_id' => '11111111111',
'app_secret' => '1111222211111112222',
'default_graph_version' => 'v2.4',
]);
$helper = $fb->getRedirectLoginHelper();
$permissions = ['public_profile','email']; // Optional permissions
$loginUrl = $helper->getLoginUrl('http://MyWebSite', $permissions);
echo '<a href="' . $loginUrl . '">Log in with Facebook!</a>';
try {
$accessToken = $helper->getAccessToken();
var_dump($accessToken);
} 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)) {
if ($helper->getError()) {
header('HTTP/1.0 401 Unauthorized');
echo "Error: " . $helper->getError() . "\n";
echo "Error Code: " . $helper->getErrorCode() . "\n";
echo "Error Reason: " . $helper->getErrorReason() . "\n";
echo "Error Description: " . $helper->getErrorDescription() . "\n";
} else {
header('HTTP/1.0 400 Bad Request');
echo 'Bad request';
}
exit;
}
// Logged in
echo '<h3>Access Token</h3>';
var_dump($accessToken->getValue());
// The OAuth 2.0 client handler helps us manage access tokens
$oAuth2Client = $fb->getOAuth2Client();
// Get the access token metadata from /debug_token
$tokenMetadata = $oAuth2Client->debugToken($accessToken);
echo '<h3>Metadata</h3>';
var_dump($tokenMetadata);
// Validation (these will throw FacebookSDKException's when they fail)
$tokenMetadata->validateAppId($config['11111111111']);
// If you know the user ID this access token belongs to, you can validate it here
//$tokenMetadata->validateUserId('123');
$tokenMetadata->validateExpiration();
if (!$accessToken->isLongLived()) {
// Exchanges a short-lived access token for a long-lived one
try {
$accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
} catch (Facebook\Exceptions\FacebookSDKException $e) {
echo "<p>Error getting long-lived access token: " . $helper->getMessage() . "</p>\n\n";
exit;
}
echo '<h3>Long-lived</h3>';
var_dump($accessToken->getValue());
}
$_SESSION['fb_access_token'] = (string)$accessToken;
but it give me this error:
但它给了我这个错误:
Facebook SDK returned an error:
Cross-site request forgery validation failed.
The "state" param from the URL and session do not match.
please any help i'm new in php and Facebook sdk's thank for any help in advance.
请提供任何帮助,我是 php 和 Facebook sdk 的新手,感谢您提前提供的任何帮助。
回答by enobrev
I found that as long as I enabled PHP sessions before generating the login url, and at the top of the script Facebook eventually redirects to, it works just fine on its own without setting a cookie (as per ale500's answer). This is using the 5.1 version of the sdk.
我发现只要在生成登录 url 之前启用 PHP 会话,并且在 Facebook 最终重定向到的脚本顶部,它就可以在不设置 cookie 的情况下正常工作(根据 ale500 的回答)。这是使用 5.1 版本的 sdk。
At the top of both scripts, I added...
在两个脚本的顶部,我添加了...
if(!session_id()) {
session_start();
}
...and it "just worked".
......它“刚刚奏效”。
Here's a barebones complete example that worked for me:
这是一个对我有用的准系统完整示例:
auth.php
身份验证文件
if (!session_id()) {
session_start();
}
$oFB = new Facebook\Facebook([
'app_id' => FACEBOOK_APP_ID,
'app_secret' => FACEBOOK_APP_SECRET
]);
$oHelper = self::$oFB->getRedirectLoginHelper();
$sURL = $oHelper->getLoginUrl(FACEBOOK_AUTH_CALLBACK, FACEBOOK_PERMISSIONS);
// Redirect or show link to user.
auth_callback.php
auth_callback.php
if (!session_id()) {
session_start();
}
$oFB = new Facebook\Facebook([
'app_id' => FACEBOOK_APP_ID,
'app_secret' => FACEBOOK_APP_SECRET
]);
$oHelper = self::$oFB->getRedirectLoginHelper();
$oAccessToken = $oHelper->getAccessToken();
if ($oAccessToken !== null) {
$oResponse = self::$oFB->get('/me?fields=id,name,email', $oAccessToken);
print_r($oResponse->getGraphUser());
}
Why?
为什么?
As an additional note, this is explained in the Docs on the repo. Look at the warning on this page.
作为附加说明,这在 repo 的文档中进行了解释。查看此页面上的警告。
Warning: The FacebookRedirectLoginHelper makes use of sessions to store a CSRF value. You need to make sure you have sessions enabled before invoking the getLoginUrl() method. This is usually done automatically in most web frameworks, but if you're not using a web framework you can add session_start(); to the top of your login.php & login-callback.php scripts. You can overwrite the default session handling - see extensibility points below.
警告:FacebookRedirectLoginHelper 使用会话来存储 CSRF 值。在调用 getLoginUrl() 方法之前,您需要确保已启用会话。这通常在大多数 Web 框架中自动完成,但如果您不使用 Web 框架,则可以添加 session_start(); 到 login.php 和 login-callback.php 脚本的顶部。您可以覆盖默认会话处理 - 请参阅下面的扩展点。
I'm adding this note because it's important to keep in mind should you happen to be running your own session management or if you're running multiple web servers in parallel. In those cases, relying upon php's default session methods won't always work.
我添加此注释是因为如果您碰巧运行自己的会话管理或并行运行多个 Web 服务器,请务必记住这一点。在这些情况下,依赖 php 的默认会话方法并不总是有效。
回答by zratan
insert this code after $helper = $fb->getRedirectLoginHelper();
在 $helper = $fb->getRedirectLoginHelper() 之后插入此代码;
$_SESSION['FBRLH_state']=$_GET['state'];
回答by G.Ashok Kumar
Lots of great answers already mentioned, here is the one which helped for me,
已经提到了很多很棒的答案,这是对我有帮助的答案,
I found that the problem is Cross-site request forgery validation failed. Required param “state” missingin FB code and here is the solution
我发现问题是跨站点请求伪造验证失败。FB 代码中缺少必需的参数“状态”,这是解决方案
After this line
这条线之后
$helper = $fb->getRedirectLoginHelper();
Add the below code,
添加以下代码,
if (isset($_GET['state'])) {
$helper->getPersistentDataHandler()->set('state', $_GET['state']);
}
回答by okdewit
I got this error while using the Facebook SDK in Symfony2, writing a Twig Extension to display data from the API in templates.
我在 Symfony2 中使用 Facebook SDK 时遇到了这个错误,编写了一个 Twig 扩展来在模板中显示来自 API 的数据。
The solution for me was adding 'persistent_data_handler'=>'session'
to the Facebook object config, which causes the state data to be stored in a session key instead of memory:
我的解决方案是添加'persistent_data_handler'=>'session'
到 Facebook 对象配置中,这会导致状态数据存储在会话密钥而不是内存中:
$fb = new Facebook\Facebook([
'app_id' => 'APP_ID',
'app_secret' => 'APP_SECRET',
'default_graph_version' => 'v2.4',
'persistent_data_handler'=>'session'
]);
By default, it was using the built-in memory handler, which didn't work properly for me. Maybe because some functions are being called from within a Twig Extension, as the memory handler does work when using the SDK exclusively in normal controllers/services.
默认情况下,它使用内置内存处理程序,这对我来说不能正常工作。可能是因为在 Twig 扩展中调用了一些函数,因为在普通控制器/服务中专门使用 SDK 时,内存处理程序确实可以工作。
Apparently the state is set when you call getLoginUrl()
, and is retrieved anytime you call getAccessToken()
. If the saved state returns null
(because your data handler isn't as persistentas it should be), the CSRF validation check fails.
显然,状态是在您调用时设置的getLoginUrl()
,并且在您调用时随时检索getAccessToken()
。如果保存的状态返回null
(因为您的数据处理程序不像它应该的那样持久),CSRF 验证检查将失败。
If you need to treat sessions in a particular way or you want to store the state somewhere else, you can also write your own handler with 'persistent_data_handler' => new MyPersistentDataHandler()
, using the FacebookSessionPersistentDataHandler as an example.
如果您需要以特定方式处理会话,或者您想将状态存储在其他地方,您还可以'persistent_data_handler' => new MyPersistentDataHandler()
使用 FacebookSessionPersistentDataHandler编写自己的处理程序,以 FacebookSessionPersistentDataHandler 为例。
回答by George
This happens when the Facebook library cannot match up the state param it receives back from Facebook with the one that it sets, by default, in the session. If you are using a framework such as Laravel, Yii2, or Kohana that implements its own session storage the standard Facebook session implementation will likely not work.
当 Facebook 库无法将它从 Facebook 收到的状态参数与其在会话中默认设置的状态参数相匹配时,就会发生这种情况。如果您使用的框架(例如 Laravel、Yii2 或 Kohana)实现了自己的会话存储,则标准的 Facebook 会话实现可能无法正常工作。
To fix it you need to create your own implementation of the PersistentDataInterface
using your framework's session library and pass this to the Facebook\Facebook
constructor.
要修复它,您需要PersistentDataInterface
使用框架的会话库创建自己的实现并将其传递给Facebook\Facebook
构造函数。
Here's an example of a Laravel persistence handler from Facebook:
这是来自Facebook的 Laravel 持久性处理程序的示例:
use Facebook\PersistentData\PersistentDataInterface;
class MyLaravelPersistentDataHandler implements PersistentDataInterface
{
/**
* @var string Prefix to use for session variables.
*/
protected $sessionPrefix = 'FBRLH_';
/**
* @inheritdoc
*/
public function get($key)
{
return \Session::get($this->sessionPrefix . $key);
}
/**
* @inheritdoc
*/
public function set($key, $value)
{
\Session::put($this->sessionPrefix . $key, $value);
}
}
Example constructor params:
示例构造函数参数:
$fb = new Facebook\Facebook([
// . . .
'persistent_data_handler' => new MyLaravelPersistentDataHandler(),
// . . .
]);
More info here: https://developers.facebook.com/docs/php/PersistentDataInterface/5.0.0
更多信息在这里:https: //developers.facebook.com/docs/php/PersistentDataInterface/5.0.0
回答by ale500
Finally, looking into FB code, I discovered that the problem
最后,查看FB代码,我发现问题
Cross-site request forgery validation failed. Required param “state” missing
跨站点请求伪造验证失败。缺少必需的参数“状态”
and similars are caused by PHP variable $_SESSION['FBRLH_state']
that for some "strange" reason when FB call the login-callback file.
和相似之处是由 PHP 变量引起的,$_SESSION['FBRLH_state']
当 FB 调用登录回调文件时,该变量出于某种“奇怪”的原因。
To solve it I store this variable "FBRLH_state"
AFTER the call of function $helper->getLoginUrl(...)
. Is very important to do only after the call of this function due to is inside this function when the variable $_SESSION['FBRLH_state']
is populated.
为了解决这个问题,我"FBRLH_state"
在调用函数之后存储了这个变量$helper->getLoginUrl(...)
。只有在调用此函数后才执行此操作非常重要,因为在$_SESSION['FBRLH_state']
填充变量时位于此函数内部。
Below an example of my code in the login.php:
下面是我在 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);
And in the login-callback.php before calling all FB code:
并在调用所有 FB 代码之前的 login-callback.php 中:
foreach ($_COOKIE as $k=>$v) {
if(strpos($k, "FBRLH_")!==FALSE) {
$_SESSION[$k]=$v;
}
}
Last, but not least, remember also to include code for PHP session so..
最后但并非最不重要的一点,请记住还要包含 PHP 会话的代码,以便..
if(!session_id()) {
session_start();
}
...
...
...
...
<?php session_write_close() ?>
I hope this response can help you to save 8-10 hours of work :) Bye, Alex.
我希望此回复可以帮助您节省 8-10 小时的工作时间 :) 再见,亚历克斯。
回答by Nino ?kopac
For me, the problem was that I wasn't running a session before the script.
对我来说,问题是我没有在脚本之前运行会话。
So, I added session_start();
before instantiating the Facebook
class.
所以,我session_start();
在实例化Facebook
类之前添加。
回答by user4520510
With Symfony, it doesn't work because the way session are managed.
使用 Symfony,它不起作用,因为会话的管理方式。
To resolve the problem you can create a new handler wich work with symfony's session.
为了解决这个问题,你可以创建一个新的处理程序,它可以与 symfony 的会话一起工作。
FacebookDataHandlerSymfony.php :
FacebookDataHandlerSymfony.php :
<?php
use Facebook\PersistentData\PersistentDataInterface;
use Symfony\Component\HttpFoundation\Session\Session;
class FacebookDataHandlerSymfony implements PersistentDataInterface
{
private $session;
public function __construct()
{
$this->session = new Session();
}
public function get($key)
{
return $this->session->get('FBRLH_' . $key);
}
public function set($key, $value)
{
$this->session->set('FBRLH_' . $key, $value);
}
}
And when you create the FB Object, you have just to specifie the new class :
当您创建 FB 对象时,您只需指定新类:
$this->fb = new Facebook([
'app_id' => '1234',
'app_secret' => '1324',
'default_graph_version' => 'v2.8',
'persistent_data_handler' => new FacebookDataHandlerSymfony()
]);
回答by usama
The same issue occurred to me on laravel 5.4 i solved this issue by putting
我在 laravel 5.4 上遇到了同样的问题,我通过放置解决了这个问题
session_start();
at the top of the script.
在脚本的顶部。
Below is the sample laravel controller namespace to give you a example how it will work.
下面是示例 laravel 控制器命名空间,为您提供它如何工作的示例。
<?php
namespace App\Http\Controllers;
session_start();
use Facebook\Facebook as Facebook;
?>
the issue is occurring because is has not yet started so by adding session start at the top of the script we are just starting the session.
出现问题是因为尚未开始,因此通过在脚本顶部添加会话开始,我们刚刚开始会话。
hope it may help somebody..
希望它可以帮助某人..
回答by Chao Chen
The Facebook object has an instance variable called persistentDataHandler
, usually an instance of FacebookSessionPersistentDataHandler
, which has a set and a get method to access the native PHP sessions.
Facebook 对象有一个名为 的实例变量persistentDataHandler
,通常是 的实例FacebookSessionPersistentDataHandler
,它有一个 set 和一个 get 方法来访问本机 PHP 会话。
When generating the callback url using:
使用以下方法生成回调 url 时:
$loginUrl = $helper->getLoginUrl($callback_url, $permissions);
The methode FacebookRedirectLoginHelper->getLoginUrl()
will create a 32-character random string, add it to the $loginUrl
and save it to the code below using the persistentDataHandler
's set method.
该方法FacebookRedirectLoginHelper->getLoginUrl()
将创建一个 32 个字符的随机字符串,将其添加到$loginUrl
并使用persistentDataHandler
的 set 方法将其保存到下面的代码中。
$_SESSION['FBRLH_' . 'state']
Later when the $helper->getAccessToken();
is called, the state param in the url will be compared to the stored in the same session to prevent CSRF. If not match, this will exception be thrown.
稍后$helper->getAccessToken();
调用时,url中的状态参数将与存储在同一会话中的状态参数进行比较以防止CSRF。如果不匹配,将抛出异常。
FacebookSDKException
:Cross-site request forgery validation failed. Required param "state" missing.
FacebookSDKException
:跨站点请求伪造验证失败。缺少必需的参数“状态”。
All these being said, you need to make sure your native PHP session feature is properly set during this process. You can check it, by adding
尽管如此,您需要确保在此过程中正确设置了本机 PHP 会话功能。您可以通过添加来检查它
die($_SESSION['FBRLH_' . 'state']);
after
后
$loginUrl = $helper->getLoginUrl($callback_url, $permissions);
and before
和之前
$accessToken = $helper->getAccessToken();
to see if that 32-character string is there.
查看是否存在该 32 个字符的字符串。
Hope it helps!
希望能帮助到你!