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 相关文章的投稿 :),做者知识技能水平有限,若是你有更好的设计方案欢迎讨论交流,若是有错误的地方也请批评指正,在此表示感谢谢谢 :)