恭喜,该插件包仅支持Google、Facebook、Twitter、Linkdln、Github和Bitbucket作为第三方登录的OAuth提供商。但是其他登录方式的集合名叫:Socialite Providers,其中包含大量针对Socialite的非官方提供商,比如国内的QQ、微信、微博等。
①、简短说明:在我研究完github的登录,写完这篇文章之后,紧接着我又开发了facebook的登录,和google的登录都走了一些弯路。
②:结论:Socialite插件已经完全满足github、facebook、google的第三方登录(基于后台服务器开发而不是前端javascript)
③、第三方登陆简单可以分为以下几个步骤,分别是:获得第三方登陆许可认证;根据规范添加页面登录样式和js实现,跳转到第三方登陆页面登陆并获取授权后,返回原指定页面(或请求);后台对登录信息进行校验,并获取对应第三方登陆用户的信息;将第三方用户信息与本系统用户进行关联。
④、研究facebook登陆的时候一开始没有使用Socialite里面的方法,等于重新原生开发。官网分为Javascript SDK和PHP SDK,参考我的另一篇文章:Javascript和PHP SDK,虽然都可以实现登录,但是局限性大,不能通用。苦逼的是我两种方法都测试了。
⑤、研究google登陆的时候一开始也是参照官方手册进行的Javascript的开发,还需要和后端配合进行数据传递和token验证。开发快完成之际,我醒悟过来。为什么又不用Socialite了呢?然后我就花了两分钟将google的Client ID和Client Secret配置到.env文件里并添加到service.php,直接访问路由就成功了。紧接着试了试facebook的登录,也可以了。我瞬间醉了,哎,虽说前功尽弃,但至少清楚了些许。弯路都走一走,才能找到最捷径的路。
这篇文章是基于github开发的通用第三方登录方法,当然facebook有专门的插件,可以参考我的另一篇文章,但是建议使用Socialite,很强大,常用登陆通吃,Laravel官方推荐,准没跑。
1、最新版本4.0要求php版本>7.0.0,否则会报错,所以避开最新版选择3.0左右的。比如composer require "laravel/socialite:~3.2"
2、创建第三方登陆管理通用的控制器SocialAuthController
我把控制器放在了Auth文件夹下面,执行命令:
php artisan make:controller Auth/SocialAuthController
也可以放在外面,执行命令:
php artisan make:controller SocialAuthController
选其一即可。注意namespace。代码如下:
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\User;
use Exception;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
class SocialAuthController extends Controller
{
/*public function __construct()
{
$this->middleware('guest');
}*/
/**
* List of providers configured in config/services acts as whitelist
*
* @var array
*/
protected $providers = [
'github',
'facebook',
'google',
'twitter'
];
/**
* Show the social login page
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function show()
{
return view('auth.social');
}
/**
* Redirect to provider for authentication
*
* @param $driver
* @return mixed
*/
public function redirectToProvider($driver)
{
if( ! $this->isProviderAllowed($driver) ) {
return $this->sendFailedResponse("{$driver} is not currently supported");
}
try {
return Socialite::driver($driver)->redirect();
} catch (Exception $e) {
// You should show something simple fail message
return $this->sendFailedResponse($e->getMessage());
}
}
/**
* Handle response of authentication redirect callback
*
* @param $driver
* @return \Illuminate\Http\RedirectResponse
*/
public function handleProviderCallback( $driver )
{
try {
$user = Socialite::driver($driver)->user();
} catch (Exception $e) {
return $this->sendFailedResponse($e->getMessage());
}
// check for email in returned user
return empty( $user->email )
? $this->sendFailedResponse("No email id returned from {$driver} provider.")
: $this->loginOrCreateAccount($user, $driver);
}
/**
* Send a successful response
*
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendSuccessResponse()
{
return redirect()->intended('home');
}
/**
* Send a failed response with a msg
*
* @param null $msg
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendFailedResponse($msg = null)
{
return redirect()->route('login')
->withErrors(['msg' => $msg ?: 'Unable to login, try with another provider to login.']);
}
protected function loginOrCreateAccount($providerUser, $driver)
{
// check for already has account
$user = User::where('email', $providerUser->getEmail())->first();
// if user already found
if( $user ) {
// update the avatar and provider that might have changed
$user->update([
'avatar' => $providerUser->avatar,
'provider' => $driver,
'provider_id' => $providerUser->id,
'access_token' => $providerUser->token
]);
} else {
// create a new user 我自己的github我获取不到账号的name,能获取到nickname。
$user = User::create([
'name' => $providerUser->getName()?:$providerUser->getNickName(),
'email' => $providerUser->getEmail(),
'avatar' => $providerUser->getAvatar(),
'provider' => $driver,
'provider_id' => $providerUser->getId(),
'access_token' => $providerUser->token,
// user can use reset password to create a password
'password' => ''
]);
}
// login the user,web版的登录,原生的auth验证
Auth::login($user, true);
return $this->sendSuccessResponse();
//API形式的登录返回值(返回JWT的token)
/*$user = BaUser::where('email', $providerUser->getEmail())->first();
if ($token = auth()->login($user)) {
$return = [
'code'=>200,
'msg'=>'登录成功',
'data'=>['token'=>'Bearer '.$token],
];
return response()->json($return);
} else {
return response()->json([
'code'=>400,
'msg'=>'登录失败',
]);
}*/
}
/**
* Check for provider allowed and services configured
*
* @param $driver
* @return bool
*/
private function isProviderAllowed($driver)
{
return in_array($driver, $this->providers) && config()->has("services.{$driver}");
}
}
3、配置 config/app.php
'providers' => [
....
Laravel\Socialite\SocialiteServiceProvider::class,
],
'aliases' => [
....
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
],
4、在你需要 的第三方登录的应用网站注册appid和appsecret,
此时可以参考学院君的github登录,虽然写得很‘简洁',https://laravelacademy.org/post/9043.html
或者翻墙网站https://www.qcode.in/oauth-login-using-facebook-google-twitter-and-github-with-laravel-socialite/ &&
https://itsolutionstuff.com/post/laravel-56-login-with-facebook-with-socialiteexample.html
5、配置config/services.php .env
//services.php
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'), // Your GitHub Client ID
'client_secret' => env('GITHUB_CLIENT_SECRET'), // Your GitHub Client Secret
'redirect' => env ( 'GITHUB_REDIRECT' ),
],
//.env
#github登录
GITHUB_CLIENT_ID = 97feba6276c
GITHUB_CLIENT_SECRET = 4d1f9b3ba873cdf02e434b1f3a78e4a
GITHUB_REDIRECT = 'http://laravel55.com/api/callback/github'
6、配置 app/User.php
迁移数据库使用,都是登陆之后需要入库的用户信息。不要再原来的迁移文件里直接书写,这里使用的方法是create不是修改table,如果运行php artisan migrate:refresh ,结构虽然会改变,但是数据都会覆盖没有啦,慎重啊兄弟姐妹们,表示被误导过,结构有了,数据没了。。。
正确操作应该是先执行:php artisan make:migration alter_users_table
然后在新增的迁移文件里添加代码,down方法是回滚时调用,懒得话就不写,写的话就和下面一样
$table->dropColumn('avatar'); 将string改为dropColumn
public function up()
{
//
Schema::table('users', function (Blueprint $table) {
//
$table->string('avatar')->nullable();
$table->string('provider', 20)->nullable();
$table->string('provider_id')->nullable();
$table->string('access_token')->nullable();
});
}
最后执行 :php artisan migrate 才正确
如果想要回滚此次操作,而且你也在down方法里配置了相应字段的回滚,那么就执行:php artisan migrate:rollback 即可恢复。
PS:另一种写法,建议上面那种,有图有真相,
php artisan make:migration alter_tablename_table --table=tablename
咱们继续,把刚才新增的字段添加到User.php里,如下所示:
protected $fillable = [
'name', 'email', 'password',
'avatar', 'provider_id', 'provider',
'access_token'
];
7、配置routes/api.php
Route::group(['middleware' => ['web']], function ($api) {
Route::get('redirect/{service}','Auth\SocialAuthController@redirectToProvider');
Route::get('callback/{service}','Auth\SocialAuthController@handleProviderCallback');
});
Route::get('/home', 'HomeController@index')->name('home');
8、跳跃到第四个,你注册了吗
记录Client ID和Client Secret()
写入.env文件配置里
#github登录
GITHUB_CLIENT_ID = 97feb6276cdbf
GITHUB_CLIENT_SECRET = 4d1fb3ba873cf02e434b1f3a78e4aee
GITHUB_REDIRECT = 'http://laravel55.com/api/callback/github' #刚才注册时填的回调地址,保持一致,且需要路由能正确访问,如果错了就删除应用重新注册,无伤大雅。
9、Create Blade File 创建登录入口
resources目录下的layouts等文件 用命令 php artisan make:auth 生成 layoyts\app.blade.php
and home.blade.php、HomeController.php等
再生成的login.blade.php文件的login按钮下面添加第三方应用登陆的按钮,如下:
<div class="card-body">
<a href="{{url('api/redirect/github')}}" class="btn btn-default btn-block">
Login with Github
</a>
</div>
10、登录时出现报错:Session store not set on request 就添加Web中间件
如上面第7个那样,感觉国内资料真的是少之又少、痛心疾首,如下老外所说
此时:跳转到github,自己创建的应用里就会多一个用户。图一是未登录github情况下的跳转,图二是已经登录github的情况下。
登陆成功会记住第三方应用的信息,再次登陆就会直接跳到这个页面,Logout会跳转到重新登录页面
11、总结
登陆之后通过SocialAuthController处理会将获取到的用户信息添加到数据库,此时多了一条刚刚用户的信息:
这就是这个登陆用户的github信息,其实facebook和google+原理都一样,通用方法和控制器已经写好了,只需要去相应网站申请appid和秘钥,在框架里进行配置就好了(.env文件和services.php文件)。比如:
https://console.developers.google.com
'google' => [
'client_id' => env('GL_ID'),
'client_secret' => env('GL_SECRET'),
'redirect' => env('APP_URL') . '/oauth/google/callback',
],
Laravel Socialite UML
我们的应用与使用OAuth标准的第三方应用的交互流程一般是这样的:
- 展示跳转到第三方应用登录的链接
- 用户点击链接跳转到第三方应用登录并进行授权
- 在用户授权后第三方应用会跳转到我们所指定的应用回调资源地址并伴随用于交互AccessToken的代码
- 我们的应用拿到Code后自主请求第三方应用并使用Code获取该用户的AccessToken
- 获取AccessToken之后的应用即可自主的从第三方应用中获取用户的资源信息
对socialite流程有疑惑的不妨自己深追源码,或者查看他人的源码分享,比如:socialite运作源码