淺談多個社交帳號的綁定設計

Dearmadman 在 Laravel Socialite 詳解 中使用 larastarscn/socialite 解決了第三方帳號登陸集成的問題,那麼在獲取到用戶資料以後呢?集成多個社交帳號,該如何綁定同一個帳號?本篇就讓咱們來探討一下集成登陸的那點事。php

起初

起初,當咱們只須要集成單個社交登陸時,咱們可能會爲了快速的完成任務簡單粗暴的在用戶模型中加入 open_id 或者 github_id 相似的屬性,那麼在數據庫中,咱們須要在表中添加相應的字段。這是能夠快速有效的完成任務的作法。git

可是,當更多的需求來臨時,要求咱們額外的集成一種或者多種社交登陸,那該怎麼辦?難道咱們還要任性的在表結構中添加相應的字段?github

Schema::table('users', function ($table) {
  $table->string('github_id');
  $table->string('douban_id');
});

這很明顯的違背了開放封閉原則,假如咱們這麼作,那麼能夠想象的當每多集成一種登陸時,咱們就須要對數據表結構作出一次修正,而且,在登陸受權回調驗證時,還要增長一道集成驅動與字段查詢匹配的工序。數據庫

那應該怎麼作?ide

設想

這麼想來,User 表是否承擔了過多的能力,它是否應該浪費本身的精力來管理這些社交標識?那不如咱們安排 SocialiteUser 來專門管理用戶與社交帳號之間的關係?咱們須要設計一種易擴展的方案來管理不一樣驅動的社交登陸,那麼咱們很容易的設計出這種表結構:this

- socialite_users
  - id
  - user_id
  - driver
  - open_id

SocialiteUser 應該具備哪些職責?很顯然,它主要用來維護社交登陸標識和用戶模型之間的關係。那麼它應該具備如下能力:spa

  • 將社交登陸帳戶綁定到用戶模型上設計

  • 獲取匹配的用戶模型3d

如下爲簡單的代碼演示:code

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class SocialiteUser extends Model
{
    public $guarded = ['id'];

    /**
     * Get user instance by driver and openid.
     *
     * @param  $driver  string
     * @param  $openid  string
     * @return /App/User|null
     */
    public function getUser($driver, $openid)
    {
        $finder =  $this->where([
            'driver' => $driver,
            'open_id' => $openid
        ])->first();

        return $finder ? $finder->user : $finder;
    }

    /**
     * get related user model.
     *
     * @return /App/User||null
     */
    public function user()
    {
        return $this->belongsTo('App\User');
    }

    /**
     * Save a new record.
     *
     * @param  $userId  integer
     * @param  $driver  string
     * @param  $id  string
     * @return /App/SocialiteUser
     */
    public function saveOne($userId, $driver, $id)
    {
        return $this->create([
            'user_id' => $userId,
            'driver' => $driver,
            'open_id' => $id
       ]);
    }
}

使用

在受權登陸流程中,用戶贊成受權,第三方應用將重定向到回調路由,回調路由中 Socialite 會主動請求獲取用戶資料,並將用戶的社交標識 ID 映射到 User 模型的 id 屬性上。

那麼咱們就能夠在回調路由中根據驅動標識和用戶相應的社交標識 ID 來匹配查詢庫中是否已存在綁定的用戶。若是存在那就直接使用匹配到的用戶登陸,若是不存在,那麼就生成一個用戶,併爲這個用戶附加社交帳戶信息。而後使用新生成的帳戶登陸。

<?php

namespace App\Http\Controllers;

use App\SocialiteUser;
use App\User;
use Socialite;

class OAuthAuthorizationController extends Controller
{
    //
    public function redirectToProvider($driver)
    {
        return Socialite::driver($driver)->redirect();
    }

    public function handleProviderCallback($driver)
    {
        $user =  Socialite::driver($driver)->user();

        $model = new User();
        $socialiteUser = new SocialiteUser();
        $finder = $socialiteUser->getUser($driver, $user->id);
        if (! $finder) {
            $finder = $model->generateUserInstance();
            $finder->save();
            $socialiteUser->saveOne($finder->id, $driver, $user->id);
        }
        
        Auth::login($finder);

        return view('home');
    }
}

這樣看來,若是需求一種新的社交登陸的集成,那麼徹底不須要作出其它代碼的改動,直接配置驅動就能夠了。

PS: 歡迎關注簡書 Laravel 專題,也歡迎 Laravel 相關文章的投稿 :),做者知識技能水平有限,若是你有更好的設計方案歡迎討論交流,若是有錯誤的地方也請批評指正,在此表示感謝謝謝 :)

相關文章
相關標籤/搜索