做完獨立的 Google+、 Facebook 與 Google 第三方登入,基本上大同小異,這次試著將它們全部整合在一起,讓使用者有更多的選擇來快速註冊與登入。

安裝 Laravel 6.x LTS 及 Authentication

參考官方網站步驟建立

composer create-project --prefer-dist laravel/laravel githubAuth "6.*"
composer require laravel/ui "^1.0" --dev
php artisan ui vue --auth
npm install && npm run dev

安裝 laravel/socialite

composer require laravel/socialite

修改 config/app.php

# providers 加入
    Laravel\Socialite\SocialiteServiceProvider::class,
# aliases 加入
    'Socialite' => Laravel\Socialite\Facades\Socialite::class,

修改 config/services.php,加入三個服務

    'google' => [
        'client_id' => env('GOOGLE_CLIENT_ID'),
        'client_secret' => env('GOOGLE_CLIENT_SECRET'),
        'redirect' => env('GOOGLE_REDIRECT'),
    ],

    'facebook' => [
        'client_id' => env('FACEBOOK_APP_ID'),
        'client_secret' => env('FACEBOOK_APP_SECRET'),
        'redirect' => env('FACEBOOK_REDIRECT'),
    ],

    'github' => [
        'client_id' => env('GITHUB_CLIENT_ID'),
        'client_secret' => env('GITHUB_CLIENT_SECRET'),
        'redirect' => env('GITHUB_REDIRECT'),
    ],

修改 route/web.php 新增

//訪客身分使用第三方登入路由,利用 {provider} 將所有的第三方登入共用同一個 redirect & callback function
Route::prefix('login')->group(function(){
    Route::get('{provider}', 'Auth\SocialController@redirect');
    Route::get('{provider}/callback', 'Auth\SocialController@callback');
});

建立 Auth\SocialController 控制器 (這次放在 Auth 目錄下)

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;

use Illuminate\Http\Request;
use Socialite;
use App\Services\SocialAccountService;
use Session;

class SocialController extends Controller
{
    /**
     * Create a redirect method to provider api.
     * 利用 Session 來傳遞 provider 變數
     */
    public function redirect($request)
    {
        Session::put('provider', $request);
        return Socialite::driver($request)->redirect();
    }
    /**
    * Return a callback method from provider api.
    * 透過 Session 找出 provider 變數並提供給 Socialite Class 使用
    * @return callback URL from provider
    */
    public function callback(SocialAccountService $service)
    {
        $provider = Session::get('provider');
        $user = $service->createOrGetUser(Socialite::driver($provider)->user());
        auth()->login($user);
        return redirect()->to('/home');
    }
}

建立 Model 及 修改相關檔案

php artisan make:model SocialAccount -m

修改 app/SocialAccount.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class SocialAccount extends Model
{
    protected $fillable = ['user_id', 'provider_user_id', 'provider'];
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

修改 create_social_accounts_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateSocialAccountsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('social_accounts', function (Blueprint $table) {
            $table->integer('user_id');
            $table->string('provider_user_id');
            $table->string('provider');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('social_accounts');
    }
}

修改完成後執行 php artisan migrate:refresh 將資料庫建立起來。

新增 app/Services 目錄及 SocialAccountService.php

<?php
namespace App\Services;
use App\SocialAccount;
use App\User;
use Laravel\Socialite\Contracts\User as ProviderUser;
use Session;

class SocialAccountService
{
    public function createOrGetUser(ProviderUser $providerUser)
    {
        //這邊透過Session將provider變數取出;
        $provider = Session::get('provider');
        $account = SocialAccount::whereProvider($provider)
            ->whereProviderUserId($providerUser->getId())
            ->first();
        if ($account) {
            return $account->user;
        } else {
            $account = new SocialAccount([
                'provider_user_id' => $providerUser->getId(),
                'provider' => $provider
            ]);
            $user = User::whereEmail($providerUser->getEmail())->first();
            if (!$user) {
                $user = User::create([
                    'email' => $providerUser->getEmail(),
                    'name' => $providerUser->getName(),
                    'password' => md5(rand(1,10000)),
                ]);
            }
            $account->user()->associate($user);
            $account->save();
            return $user;
        }
    }
}

修改 login 視圖,新增三個按鈕

<div class="form-group row mt-3">
    <div class="col-md-3 offset-md-3">
        <a href="{{url('/login/google')}}" class="btn btn-danger">Google+</a>
    </div>
    <div class="col-md-3">
        <a href="{{url('/login/facebook')}}" class="btn btn-primary">Facebook</a>
    </div>
    <div class="col-md-3">
        <a href="{{url('/login/github')}}" class="btn btn-secondary">Github</a>
    </div>
</div>

編輯 .env 到 三個 Provider 修改 callback 位置。

GOOGLE_CLIENT_ID = XXXXXXXXXXXXXX
GOOGLE_CLIENT_SECRET = XXXXXXXXXXXX
GOOGLE_REDIRECT = https://localhost/login/google/callback

FACEBOOK_APP_ID = XXXXXXXXXXXXXXX
FACEBOOK_APP_SECRET = XXXXXXXXXXXXXXXXX
FACEBOOK_REDIRECT = https://localhost/login/facebook/callback

GITHUB_CLIENT_ID = XXXXXXXXXXXXXX
GITHUB_CLIENT_SECRET = XXXXXXXXXXXXXXXX
GITHUB_REDIRECT = https://localhost/login/github/callback

測試

從資料庫中可以看到使用了三種登入方式,因為我使用相同的 email 所以 user_id 都是 1。

成品同步放在我的 GitHub 上.

https://github.com/weayzehoa/socialiteAuth

最後修改日期: 2020 年 10 月 12 日