Facebook 網頁登入基於 Facebook 的 PHP-graph-sdk

xiaoyukarl發表於2018-12-25

第一次使用facebook登入, 也踩了一些坑,寫篇文章記錄一下吧,避免各位coder少踩一些坑
本篇文章是基於laravel5.5寫的,如果用其他框架,可以自己參考著修改
註冊了一個企業facebook的賬號,申請一個應用,根據要求填寫資訊即可
facebook應用建立地址:"https://developers.facebook.com/apps/"
應用要求:
1.伺服器需要在境外或者伺服器能翻牆
2.網站需要是https

如果大家是用laravel或者一些新的框架的話,可以直接使用composer下載php-sdk包

composer require facebook/graph-sdk

github地址是:"https://github.com/facebook/php-graph-sdk"

配置facebook應用資訊:
例如我的程式碼:
在config目錄下有一個site.php的配置檔案,在檔案中加上facebook配置

 'facebook' => [
        'app_id' => '2176*****64007',
        'app_secret' => '2ec436b*******2f1046a73d72150',
        'default_graph_version' => 'v2.10'
    ]

在loginController控制器下增加兩個方法
//facebook_login和facebook_callback
增加兩個路由

    Route::get('facebook/login','LoginController@facebook_login')->name('facebook.login');
    Route::match(['get','post'],'facebook/callback','LoginController@facebook_callback')->name('facebook.callback');

facebook_login方法

/**
     * facebook 登入
     * @param Request $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function facebook_login(Request $request)
    {
        session_start();//啟用session,防止facebook報錯
        $facebookConfig = config('site.facebook');
        $fb = new Facebook($facebookConfig);
        $helper = $fb->getRedirectLoginHelper();
        $permissions = ['email']; // Optional permissions
        $facebookRedirectUrl = route('home.facebook.callback');//facebook的回撥方法,參考第二個路由,千萬不要直接copy我的程式碼
        $facebookLoginUrl = $helper->getLoginUrl($facebookRedirectUrl,$permissions);
        return redirect($facebookLoginUrl);
    }

facebook_callback方法內容

/**
     * facebook 登入回撥
     * @param Request $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     * @throws \Facebook\Exceptions\FacebookSDKException
     */
    public function facebook_callback(Request $request)
    {
        session_start();//啟用session,防止facebook報錯,不啟用session會報錯,可以自行嘗試,不會最好了
        if($request->get('error') == 'access_denied'){
            return redirect('/');
        }
        $facebookConfig = config('site.facebook');
        //注意這裡的坑,有很多人的guzzle版本都是6.*的,現在的facebook的sdk只能支援guzzle5.*,請檢查你的版本
        $facebookConfig['http_client_handler'] = 'guzzle';//如果是5.*,加上這句話就可以了
        //如果是guzzle6.*,解決方法有兩個,一是降低guzzle的版本,然後使用上面那句話,二是重寫請求
        //請求重寫的參考,Guzzle6HttpClient這個類寫在下面
        /**
        $client = new GuzzleHttp\Client;
        $facebookConfig['http_client_handler'] = new Guzzle6HttpClient($client);
        **/
        $fb = new Facebook($facebookConfig);
        $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
            $message = 'Graph returned an error: ' . $e->getMessage();
            Log::warning('facebook login failed',['error'=>$message]);//Log的名稱空間沒加,寫的時候自己加一下
            return back()->withErrors([$message]);
        } catch(Facebook\Exceptions\FacebookSDKException $e) {
            // When validation fails or other local issues
            $message = 'Facebook SDK returned an error: ' . $e->getMessage();
            Log::warning('facebook login failed',['error'=>$message]);
            return back()->withErrors([$message]);
        }

        if (! isset($accessToken)) {
            if ($helper->getError()) {
                $message = "Error: " . $helper->getError() . "\n";
                $message .= "Error Code: " . $helper->getErrorCode() . "\n";
                $message .= "Error Reason: " . $helper->getErrorReason() . "\n";
                $message .= "Error Description: " . $helper->getErrorDescription() . "\n";
            } else {
                $message = 'Bad request';
            }
            Log::warning('facebook login failed',['error'=>$message]);
            return abort(401);
        }

        $oAuth2Client = $fb->getOAuth2Client();
        $tokenMetadata = $oAuth2Client->debugToken($accessToken);
        $tokenMetadata->validateAppId(config('site.facebook.app_id'));
        $tokenMetadata->validateExpiration();

        if (! $accessToken->isLongLived()) {
            try {
                $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
            } catch (Facebook\Exceptions\FacebookSDKException $e) {
                $message = 'Error getting long-lived access token'. $e->getMessage();
                Log::warning('facebook login failed',['error'=>$message]);
                return back()->withErrors([$message]);
            }
        }
        $_SESSION['fb_access_token'] = (string) $accessToken;
        $fb->setDefaultAccessToken($accessToken);
        $response = $fb->get('/me?locale=en_US&fields=id,name,email');
        $userNode = $response->getGraphUser();
        $email = $userNode->getField('email');
        $name = $userNode->getField('name');
        $fb_user_id = $userNode->getField('id');
        //拿到這些內容之後,再根據自己的邏輯進行處理

    }

Guzzle6HttpClient類的重寫參考
在vendor->facebook->graph-sdk->src->Facebook->HttpClients目錄寫新增一個類Guzzle6HttpClient
內容如下:

namespace Facebook\HttpClients;

use Facebook\Exceptions\FacebookSDKException;
use Facebook\Http\GraphRawResponse;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request;

class Guzzle6HttpClient implements FacebookHttpClientInterface
{

    private $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    public function send($url, $method, $body, array $headers, $timeOut)
    {
        $request = new Request($method, $url, $headers, $body);
        try {
            $response = $this->client->send($request, ['timeout' => 60, 'http_errors' => false]);
        } catch (RequestException $e) {
            throw new FacebookSDKException($e->getMessage(), $e->getCode());
        }
        $httpStatusCode = $response->getStatusCode();
        $responseHeaders = $response->getHeaders();

        foreach ($responseHeaders as $key => $values) {
            $responseHeaders[$key] = implode(', ', $values);
        }

        $responseBody = $response->getBody()->getContents();

        return new GraphRawResponse($responseHeaders, $responseBody, $httpStatusCode);
    }
}

搞完這些還需要在應用裡面修改配置,把facebook_callback的回撥連結填寫在應用配置有效 OAuth 跳轉 URI下
最後測試一下是否能拿到facebook的使用者資訊
如果拿到了恭喜你很順利,要是沒有,再根據錯誤資訊進行修正吧

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章