我正在尝试使用像这样的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;
但它给了我这个错误:
Facebook SDK returned an error:
Cross-site request forgery validation failed.
The "state" param from the URL and session do not match.
请任何帮助我是新的PHP和Facebook sdk感谢您提前提供任何帮助。
答案 0 :(得分:94)
我发现只要我在生成登录URL之前启用了PHP会话,并且在Facebook最终重定向到脚本的顶部,它就可以自行运行而无需设置cookie(as per ale500's answer)。这是使用sdk的5.1版本。
在两个脚本的顶部,我添加了......
if(!session_id()) {
session_start();
}
......而且#34;刚刚工作了#34;。
这是一个适合我的准系统完整示例:
<强> 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 强>
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());
}
作为补充说明,这在回购文件中有解释。查看this page上的警告。
警告:FacebookRedirectLoginHelper使用会话来存储CSRF值。在调用getLoginUrl()方法之前,您需要确保已启用会话。这通常在大多数Web框架中自动完成,但如果您不使用Web框架,则可以添加session_start();在你的login.php&amp;的顶部login-callback.php脚本。您可以覆盖默认会话处理 - 请参阅下面的扩展点。
我要添加此注释,因为如果您正好运行自己的会话管理或者并行运行多个Web服务器,请务必记住这一点。在这些情况下,依靠php的默认会话方法不会一直有效。
答案 1 :(得分:63)
之后插入此代码 $ helper = $ fb-&gt; getRedirectLoginHelper();
$_SESSION['FBRLH_state']=$_GET['state'];
答案 2 :(得分:47)
已经提到了很多很棒的答案,这是对我有帮助的答案,
我发现问题是跨站点请求伪造验证失败。 FB代码中必需的参数“状态”缺失,这是解决方案
此行之后
$helper = $fb->getRedirectLoginHelper();
添加以下代码,
if (isset($_GET['state'])) {
$helper->getPersistentDataHandler()->set('state', $_GET['state']);
}
答案 3 :(得分:20)
我在Symfony2中使用Facebook SDK时遇到此错误,编写了一个Twig Extension来显示模板中API的数据。
我的解决方案是将'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'
]);
默认情况下,它使用的内置内存处理程序对我来说无法正常工作。也许是因为某些函数是在Twig扩展中调用的,因为内存处理程序在普通控制器/服务中专门使用SDK时可以正常工作。
显然,当您拨打getLoginUrl()
时会设置状态,并在您拨打getAccessToken()
时随时检索状态。如果保存的状态返回null
(因为您的数据处理程序不是 persistent ),则CSRF验证检查失败。
如果您需要以特定方式处理会话,或者您希望将状态存储在其他位置,您还可以使用Facebook 'persistent_data_handler' => new MyPersistentDataHandler()
编写自己的处理程序,并使用FacebookSessionPersistentDataHandler作为示例。
答案 4 :(得分:16)
当Facebook库无法将其从Facebook收到的状态参数与默认情况下在会话中设置的状态参数匹配时,就会发生这种情况。如果您使用的是Laravel,Yii2或Kohana等框架来实现自己的会话存储,则标准的Facebook会话实现可能无法正常工作。
要修复它,您需要使用框架的会话库创建自己的PersistentDataInterface
实现,并将其传递给Facebook\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);
}
}
示例构造函数参数:
$fb = new Facebook\Facebook([
// . . .
'persistent_data_handler' => new MyLaravelPersistentDataHandler(),
// . . .
]);
更多信息: https://developers.facebook.com/docs/php/PersistentDataInterface/5.0.0
答案 5 :(得分:8)
最后,看一下FB代码,我发现了问题
跨站点请求伪造验证失败。必需的参数“状态”缺失
和similars是由PHP变量$_SESSION['FBRLH_state']
引起的,当FB调用login-callback文件时出于某种“奇怪”的原因。
要解决此问题,我会在调用函数"FBRLH_state"
之后存储此变量$helper->getLoginUrl(...)
。只有在调用此函数后才能执行此操作,因为当填充变量$_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个小时的工作:) 再见,亚历克斯。
答案 6 :(得分:7)
对我来说,问题是我没有在脚本之前运行会话。
因此,我在实例化session_start();
类之前添加了Facebook
。
答案 7 :(得分:4)
我在laravel 5.4上遇到了同样的问题。我通过
解决了这个问题session_start();
位于脚本的顶部。
下面是示例laravel控制器命名空间,为您提供一个示例,它将如何工作。
<?php
namespace App\Http\Controllers;
session_start();
use Facebook\Facebook as Facebook;
?>
问题正在发生,因为还没有开始,所以通过在我们刚开始会话的脚本顶部添加会话开始。
希望它可以帮助某人......答案 8 :(得分:3)
Facebook对象有一个名为persistentDataHandler
的实例变量,通常是FacebookSessionPersistentDataHandler
的实例,它有一个set和get方法来访问本机PHP会话。
使用以下方式生成回调网址时
$loginUrl = $helper->getLoginUrl($callback_url, $permissions);
方法FacebookRedirectLoginHelper->getLoginUrl()
将创建一个32个字符的随机字符串,将其添加到$loginUrl
并使用persistentDataHandler
的set方法将其保存到下面的代码中。
$_SESSION['FBRLH_' . 'state']
稍后调用$helper->getAccessToken();
时,url中的状态参数将与存储在同一会话中的状态参数进行比较,以防止CSRF。如果不匹配,则会抛出异常。
FacebookSDKException
:跨网站请求伪造验证失败。必需的参数&#34;州&#34;丢失。
所有这些都说明了,您需要确保在此过程中正确设置了本机PHP会话功能。你可以通过添加
来检查它die($_SESSION['FBRLH_' . 'state']);
后
$loginUrl = $helper->getLoginUrl($callback_url, $permissions);
之前
$accessToken = $helper->getAccessToken();
查看是否有32个字符的字符串。
希望它有所帮助!
答案 9 :(得分:2)
使用Symfony,它不起作用,因为管理会话的方式。
要解决此问题,您可以创建一个与symfony会话一起使用的新处理程序。
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);
}
}
当您创建FB对象时,您只需要指定新类:
$this->fb = new Facebook([
'app_id' => '1234',
'app_secret' => '1324',
'default_graph_version' => 'v2.8',
'persistent_data_handler' => new FacebookDataHandlerSymfony()
]);
答案 10 :(得分: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.com或http://www.example.com进入您的网站,它将始终与您在应用设置中设置的内容相匹配。为什么?因为$ _SERVER ['SERVER_NAME']将返回包含或不包含www的域名,具体取决于用户在浏览器中输入网址的方式。
这是我的发现,这是关于我唯一没有取消CSRF检查的事情,到目前为止,没有任何问题。
我希望这会有所帮助。
答案 11 :(得分:2)
如果源主机名与经过身份验证的目标主机名不同,则会收到此错误。
$loginUrl = $helper->getLoginUrl('http://MyWebSite', $permissions);
使用此声明,如果您网站上的访问者使用http://www.mywebsite.com/,则会引发跨网站错误。
您必须确保源和目标主机名完全相同,包括最终的www前缀。
修正版:
$loginUrl = $helper->getLoginUrl('http://'.$_SERVER['SERVER_NAME'], $permissions);
答案 12 :(得分:2)
您可以这样设置具有新状态的会话
<?php
if(isset($_GET['state'])) {
if($_SESSION['FBRLH_' . 'state']) {
$_SESSION['FBRLH_' . 'state'] = $_GET['state'];
}
}
?>
答案 13 :(得分:1)
轻松解决我的问题。 我改变了:
$loginUrl = $helper->getLoginUrl('http://www.MYWEBSITE.ca/index_callback.php', $permissions);
为:
$loginUrl = $helper->getLoginUrl('http://MYWEBSITE.ca/index_callback.php', $permissions);
删除'www'
解决了问题。
答案 14 :(得分:1)
在我的情况下,我检查了错误,发现错误导致我执行代码的解决方案:
date_default_timezone_set('Europe/Istanbul');
在脚本之前。工作就像一个魅力。对于您的位置检查:timezones.europe.php
答案 15 :(得分:0)
这是很多人在FB Api面临的常见问题。这只是一个SESSION问题。要解决此问题,请添加一些代码,例如。
在回调脚本上通常fb-callback.php添加“session_start();”就在你加入facebook自动加载文件之前。然后“$ _SESSION ['FBRLH_state'] = $ _ GET ['state'];”在“$ helper = $ fb-&gt; getRedirectLoginHelper();”之后线。
示例:
<?php
session_start();
include 'vendor/autoload.php';
include 'config.php'; /*Facebook Config*/
$helper = $fb->getRedirectLoginHelper();
$_SESSION['FBRLH_state']=$_GET['state'];
try {
$accessToken = $helper->getAccessToken();
} ?>
答案 16 :(得分:0)
我正在与Symfony玩耍,遇到这个问题,一次尝试是,下一次尝试。 我通过将facebookRedirectLoginHelper对象存储到会话中并稍后在会话中检索它来解决此问题,而不是再次询问Facebook对象。
documentation (在撰写本文时为Symfony 4.3)规定如下:
在使用Session类之前,请确保尚未启动PHP会话。如果您有一个旧的会话系统开始您的会话,请参阅旧会话。
Symfony会话旨在替换一些本地PHP函数。应用程序应避免使用session_start(),session_regenerate_id(),session_id(),session_name()和session_destroy(),而应使用以下部分中的API。
虽然建议显式启动会话,但实际上会话将按需启动,也就是说,如果发出任何会话请求以读取/写入会话数据。
所以我认为从会话中检索对象本质上是启动php会话。
如果您正在使用某些php框架,请记住这一点。
答案 17 :(得分:0)
对我来说,设置会话状态
完整代码(在redirect url php中)
$accessToken = '';
$helper = $fb->getRedirectLoginHelper();
if(isset($_GET['state'])){
$_SESSION['FBRLH_state']=$_GET['state'];
}
try {
$accessToken = $helper->getAccessToken();
} catch ( Facebook\Exceptions\FacebookResponseException $e ) {
// When Graph returns an error
echo 'Graph returned an error: ' . $e->getMessage();
}
答案 18 :(得分:0)
间歇性问题的解决方案
我是a)重定向到Facebook登录链接,b)从login.php重定向到main.php。用户将前往main.php和其他几个页面,然后在浏览器中单击返回。
最终,他们会点击login.php并发布一堆信用卡,但Facebook会在一次成功后删除$ _SESSION [&#39; FBRLH_state&#39;],所以即使它有正确的$ _GET [&#39;州&#39;],它会出错。
解决方案是a)在内部跟踪用户是否已登录并避免在login.php中重复Facebook逻辑,或者b)跟踪该特定用户的所有最近有效状态参数(在某个会话中)由Facebook设置,如果$ _GET [&#39; state&#39;]在该数组中,那么执行此操作:
$ _ SESSION [&#39; FBRLH_state&#39;] = $ _GET [&#39;州&#39;];
在这种情况下,您可以安全地执行此操作,而不会破坏CSRF保护。
答案 19 :(得分:0)
可以帮助在前端使用 Javascript Helper 的用户对用户进行身份验证,而在PHP中,可以尝试从重定向登录帮助<重定向登录帮助<中提取 access_token / strong>即可。所以使用以下
getJavaScriptHelper();
而不是
getRedirectLoginHelper();
答案 20 :(得分:0)
我有同样的错误,因为我忘记添加“www”。到发件人地址。在Client-OAuth设置中,必须有正确的名称。
答案 21 :(得分:0)
适用于我的Yii2解决方案:
use Facebook\PersistentData\PersistentDataInterface;
use Yii;
class PersistentDataHandler implements PersistentDataInterface
{
/**
* @var string Prefix to use for session variables.
*/
protected $sessionPrefix = 'FBRLH_';
public function get($key)
{
return Yii::$app->session->get($this->sessionPrefix . $key);
}
public function set($key, $value)
{
Yii::$app->session->set($this->sessionPrefix . $key, $value);
}
}
答案 22 :(得分:0)
如果origin hostname与目标主机名不同,则会触发错误[如Souch所述]。当访问者在URL地址框中键入“http://website.com”时,与“http://www.website.com”不同。我们将它们重定向到正确的URL,方法是在session_start()之前的最高位置添加以下代码。
if($_SERVER['HTTP_HOST']!=='www.website.com'){
header('location: http://www.website.com');
}
答案 23 :(得分:-1)
对我来说,问题是不同的; (我真蠢) 我有一个带有Facebook登录的弹出窗口,以及登录/注册页面中的Facebook登录按钮。
原因是我重新实例化了Facebook和getRedirectLoginHelper对象。我必须在登录/注册页面中注释掉这些语句,因为它已经可用。
$_SESSION['FBRLH_state']=$_GET['state'];
不是要走的路。虽然它奏效了。
答案 24 :(得分:-1)
如果您仍然看到错误,则只需清除浏览器缓存。 使用enobrev解决方案解决问题后,我继续遇到该错误。 一段时间后,我了解到我需要清理缓存,并且在重新启动浏览器后,它可以正常工作!