Laravel第三方登入-Facebook登入(二)

Laravel結合Facebook登入

前一篇: Laravel第三方登入-Facebook登入(一)

Facebook登入控制器

在使用laravel/socialite套件做Facebook登入功能時,指定使用facebook當作這次的第三方認證驅動,並設定重新導向的網址,即可連結到取得Facebook授權的網址

app/Http/Controllers/UserAuthController.php

<?PHP
namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Validator; //驗證器
use Hash; //雜湊
use Mail; //寄信
use Socialite;
use App\Shop\Entity\User; //使用者 Eloquent ORM Model

class UserAuthController extends Controller
{
    //Facebook登入
    public function facebookSignInProcess()
    {
        $redirect_url = env('FB_REDIRECT');

        return Socialite::driver('facebook')
            ->scopes(['user_friends'])
            ->redirectUrl($redirect_url)
            ->redirect();
    }
}
?>

resources/views/layout/master.blade.php

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>@yield('title') - Shop Laravel</title>
        <script src="http://cdn.bootcss.com/jquery/1.11.0/jquery.min.js" type="text/javascript"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
        <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
        <link href=" https://netdna.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css " rel="stylesheet" type="text/css" />
        <link href="/css/style.css" rel="stylesheet" type="text/css" />
    </head>
    <boby>
        <header>
            <ul class="nav">
                <li><a href="/">回首頁</a></li>
                @if(session()->has('user_id'))
                    <li><a href="/user/auth/sign-out">登出</a></li>
                @else
                    <li><a href="/user/auth/sign-in">登入</a></li>
                    <li><a href="/user/auth/facebook-sign-in">Facebook登入</a></li>
                    <li><a href="/user/auth/sign-up">註冊</a></li>
                @endif
            </ul>
        </header>
        <div class="container">
            @yield('content')
        </div>
        <footer>
            <a href="#">聯絡我們</a>
        </footer>
    </body>
</html>

在連結到 /user/auth/facebook-sign-in(get)取得Facebook授權網址後,若成功的話,會看到下方允許授權的畫面

首頁畫面

Facebook登入畫面

會出現以下畫面,有稍微看一下審查的說明,基本上如果是開發人員測試用的不需要送審查,功能不會受到影響。

取得Facebook授權資料

取得Facebook授權資料後,Facebook會將我們重新導向到我們設定的重新導向網址 /user/auth/facebook-sign-in-callback(get),我們可以在這個網址中處理回傳授權資料,取得授權資料的控制器會長得向下方這樣
app/Http/Controllers/UserAuthController.php

<?PHP
namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Validator; //驗證器
use Hash; //雜湊
use Mail; //寄信
use Socialite;
use App\Shop\Entity\User; //使用者 Eloquent ORM Model

class UserAuthController extends Controller
{
    //Facebook登入重新導向授權資料處理
    public function facebookSignInCallbackProcess()
    {
        if(request()->error=="access_denied")
        {
            throw new Exception('授權失敗,存取錯誤');
        }
        //依照網域產出重新導向連結 (來驗證是否為發出時同一callback)
        $redirect_url = env('FB_REDIRECT');
        //取得第三方使用者資料
        /*
        $user = Socialite::driver('facebook')->user();
        var_dump($user);
        //*/
        $FacebookUser = Socialite::driver('facebook')
            ->fields([
                'name',
                'email',
            ])
            ->redirectUrl($redirect_url)->user();
       
        $facebook_email = $FacebookUser->email;

        if(is_null($facebook_email))
        {
            throw new Exception('未授權取得使用者 Email');
        }
        //取得 Facebook 資料
        $facebook_id = $FacebookUser->id;
        $facebook_name = $FacebookUser->name;

        echo "facebook_id=".$facebook_id.", facebook_name=".$facebook_name;
        //*/
    }
}
?>

Facebook只允許指定的網址能夠存取授權資料,所以在取得授權資料時,一樣要設定重新導向的網址是什麼,確認是否有權限取得授權資料,設定完成後使用 ->user() 去取得Facebook使用者資料物件

$redirect_url = env('FB_REDIRECT');
$FacebookUser = Socialite::driver('facebook')
    ->fields([
        'name',
        'email',
    ])
    ->redirectUrl($redirect_url)->user();

因為我們設定使用者的E-mail為必要欄位,當使用者沒有授權我們存取Email時,我們則會丟出例外,終止Facebook登入程序,若授權成功時,我們將取得Facebook使用者資料中的$facebook_id及$facebook_name用來登入或註冊會員
 

$facebook_email = $FacebookUser->email;

if(is_null($facebook_email))
{
    throw new Exception('未授權取得使用者 Email');
}
//取得 Facebook 資料
$facebook_id = $FacebookUser->id;
$facebook_name = $FacebookUser->name;

操作步驟

首頁,點擊Facebook登入

輸入Facebook帳號密碼

回傳結果,有正確抓到資料

更新Facebook ID或註冊新會員

當取得Facebook授權資料的$facebook_id及$facebook_email後,就要去使用者資料撈取資料,看有沒有已存在的使用者資料,若沒有存在使用者資料,則註冊一筆新的使用者資料
app/Http/Controllers/UserAuthController.php

<?PHP
namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Validator; //驗證器
use Hash; //雜湊
use Mail; //寄信
use Socialite;
use App\Shop\Entity\User; //使用者 Eloquent ORM Model

class UserAuthController extends Controller
{
    //Facebook登入重新導向授權資料處理
    public function facebookSignInCallbackProcess()
    {
        //...以上省略...
        $facebook_email = $FacebookUser->email;
        if(is_null($facebook_email))
        {
            throw new Exception('未授權取得使用者 Email');
        }
        //取得 Facebook 資料
        $facebook_id = $FacebookUser->id;
        $facebook_name = $FacebookUser->name;

        //取得使用者資料是否有此Facebook_id資料
        $User = User::where('facebook_id', $facebook_id)->first();

        if(is_null($User))
        {
            //沒有綁定Facebook Id的帳號,透過Email尋找是否有此帳號
            $User = User::where('email', $facebook_email)->first();
            if(!is_null($User))
            {
                //有此帳號,綁定Facebook Id
                $User->facebook_id = $facebook_id;
                $User->save();
            }
        }

        if(is_null($User))
        {
            //尚未註冊
            $input = [
                'email' => $facebook_email, //E-mail
                'nickname' => $facebook_name, //暱稱
                'password' => uniqid(), //隨機產生密碼
                'facebook_id' => $facebook_id, //Facebook ID
                'type' => 'G', //一般使用者
            ];
            //密碼加密
            $input['password'] = Hash::make($input['password']);
            //新增會員資料
            $User = User::create($input);

            //寄送註冊通知信
            $mail_binding = [
                'nickname' => $input['nickname']
            ];

            Mail::send('email.signUpEmailNotification', $mail_binding,
                function($mail) use ($input){
                    $mail->to($input['email']);
                    $mail->from('henrychang0202@gmail.com');
                    $mail->subject('恭喜註冊 Shop Laravel 成功');
                });
        }

        echo "登入成功!";
    }
}
?>

在Facebook登入功能時,會優先撈取是否資料庫有對應$facebook_id的使用者資料,若有的話以此筆資料為主,若沒有此$facebook_id的使用者,則會再透過$facebook_email撈取是否資料庫有對應的使用者資料,若有的話則將此$facebook_id綁定更新到這個會員的資料欄位中,這樣下次就可以直接透過$facebook_id取得資料進入登入了。
 

//取得使用者資料是否有此Facebook_id資料
$User = User::where('facebook_id', $facebook_id)->first();

if(!is_null($User))
{
    //沒有綁定Facebook Id的帳號,透過Email尋找是否有此帳號
    $User = User::where('email', $facebook_email)->first();
    if(!is_null($User))
    {
        //有此帳號,綁定Facebook Id
        $User->facebook_id = $facebook_id;
        $User->save();
    }
}

當透過$facebook_id及$facebook_email都沒有辦法取得使用者的資料時,表示這位使用者從未註冊過網站的帳號,此時就必須要自動的建立該會員的帳號資料,隨機產生一組密碼,並將該帳號類型設定為一般使用者。當註冊完成後,一樣寄送註冊通知信給該會員,通知已經註冊成功的訊息。
 

else
{
    //尚未註冊
    $input = [
        'email' => $facebook_email, //E-mail
        'nickname' => $facebook_name, //暱稱
        'password' => uniqid(), //隨機產生密碼
        'facebook_id' => $facebook_id, //Facebook ID
        'type' => 'G', //一般使用者
    ];
    //密碼加密
    $input['password'] = Hash::make($input['password']);
    //新增會員資料
    $User = User::create($input);

    //寄送註冊通知信
    $mail_binding = [
        'nickname' => $input['nickname']
    ];

    Mail::send('email.signUpEmailNotification', $mail_binding,
        function($mail) use ($input){
            $mail->to($input['email']);
            $mail->from('henrychang0202@gmail.com');
            $mail->subject('恭喜註冊 Shop Laravel 成功');
    });
}

信件畫面

Facebook登入網站

不管是$facebook_id及$facebook_email有沒有找到會員資料,若沒有找到也會新增一筆會員資料,為了能夠提高使用者的體驗,在處理完會員資料後,就要立即幫使用者作登入的動作,所以直接使用session()->put()的方法在Session記錄使用者的編號就可以直接登入了,當登入完成後重新導向到首頁即可。
app/Http/Controllers/UserAuthController.php
 

<?PHP
namespace App\Http\Controllers;

use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Validator; //驗證器
use Hash; //雜湊
use Mail; //寄信
use Socialite;
use App\Shop\Entity\User; //使用者 Eloquent ORM Model

class UserAuthController extends Controller
{
    //Facebook登入重新導向授權資料處理
    public function facebookSignInCallbackProcess()
    {
        //...以上省略...

        //登入會員
        //Session記錄會員編號
        session()->put('user_id', $User->id);

        //重新導向到原先使用者造訪頁面,沒有嘗試造訪頁則重新導向回首頁
        return redirect()->intended('/');
    }

?>

 

小結

在這個社群登入的新的需求案例中,我們可以知道當我們要做資料表的異動時,一樣可以透過資料庫遷移(Migration)來達成資料表的異動,做好有效的資料表版本控管的工作,所以在往後若還有新的需求要做資料表的異動,一樣可以透過這樣的方式去達成資料表異動及管理的效果。

第三方登入幾乎是現在不同專案中必備的功能了,所以Laravel官方也貼心的開發laravel/socialite套件讓我們可以很快速地處理第三方登入的需求。透過這樣的範例也可以很快的了解Laravel如何實作第三方登入的功能。