什么是 Laravel Socialite
Laravel Socialite 是官方提供的包,用于简洁地实现基于 OAuth 2.0 的社交登录。它已支持 GitHub、Google、Facebook、X(Twitter)、LinkedIn 等主流 OAuth 提供方,只需数行代码就能完成原本复杂的 OAuth 实现。
内置支持的提供方如下:
提供方 键名 Bitbucket bitbucketFacebook facebookGitHub githubGitLab gitlabGoogle googleLinkedIn (OpenID) linkedin-openidSlack slack / slack-openidSpotify spotifyTwitch twitchX (Twitter) x
社交登录的流程
使用 Composer 安装:
composer require laravel/socialite
升级 Socialite 大版本时请务必查看升级指南 。
config/services.php
在 config/services.php 中添加各提供方的客户端 ID、密钥及回调 URL。
// config/services.php
'github' => [
'client_id' => env ( 'GITHUB_CLIENT_ID' ),
'client_secret' => env ( 'GITHUB_CLIENT_SECRET' ),
'redirect' => env ( 'GITHUB_REDIRECT_URI' ),
],
'google' => [
'client_id' => env ( 'GOOGLE_CLIENT_ID' ),
'client_secret' => env ( 'GOOGLE_CLIENT_SECRET' ),
'redirect' => env ( 'GOOGLE_REDIRECT_URI' ),
],
'facebook' => [
'client_id' => env ( 'FACEBOOK_CLIENT_ID' ),
'client_secret' => env ( 'FACEBOOK_CLIENT_SECRET' ),
'redirect' => env ( 'FACEBOOK_REDIRECT_URI' ),
],
在 redirect 中使用相对路径时会被自动解析为完整 URL。
.env
通过环境变量管理凭据。下面以 GitHub 为例:
GITHUB_CLIENT_ID =your-client-id
GITHUB_CLIENT_SECRET =your-client-secret
GITHUB_REDIRECT_URI =https://example.com/auth/github/callback
在 GitHub 中,可以在 GitHub Developer Settings 创建 OAuth App 来获得 Client ID 与密钥。
认证流程
OAuth 认证需要两个路由:一个用于重定向,一个用于回调。
use Laravel\Socialite\Facades\ Socialite ;
// 将用户重定向到 GitHub
Route :: get ( '/auth/github' , function () {
return Socialite :: driver ( 'github' ) -> redirect ();
});
// 处理来自 GitHub 的回调
Route :: get ( '/auth/github/callback' , function () {
$user = Socialite :: driver ( 'github' ) -> user ();
// 通过 $user->token 获取访问令牌
});
保存用户并登录
在回调路由中获取用户信息,保存到数据库后登录。
use App\Models\ User ;
use Illuminate\Support\Facades\ Auth ;
use Laravel\Socialite\Facades\ Socialite ;
Route :: get ( '/auth/github/callback' , function () {
$githubUser = Socialite :: driver ( 'github' ) -> user ();
$user = User :: updateOrCreate (
[ 'github_id' => $githubUser -> id ],
[
'name' => $githubUser -> name ,
'email' => $githubUser -> email ,
'github_token' => $githubUser -> token ,
'github_refresh_token' => $githubUser -> refreshToken ,
]
);
Auth :: login ( $user );
return redirect ( '/dashboard' );
});
使用 updateOrCreate 时,users 表中必须存在 github_id 列。请参考后文的迁移示例。
获取用户信息
user() 方法返回的对象包含如下属性和方法。
Route :: get ( '/auth/github/callback' , function () {
$user = Socialite :: driver ( 'github' ) -> user ();
// OAuth 2.0 提供方
$token = $user -> token ;
$refreshToken = $user -> refreshToken ;
$expiresIn = $user -> expiresIn ;
// OAuth 1.0 提供方(如 X)
$token = $user -> token ;
$tokenSecret = $user -> tokenSecret ;
// 所有提供方通用
$user -> getId ();
$user -> getNickname ();
$user -> getName ();
$user -> getEmail ();
$user -> getAvatar ();
});
通过访问令牌获取用户
如果已有访问令牌,可用 userFromToken() 获取用户信息。
$user = Socialite :: driver ( 'github' ) -> userFromToken ( $token );
无状态模式
对于不使用 Cookie Session 的 API,可以通过 stateless() 关闭状态校验。
return Socialite :: driver ( 'google' ) -> stateless () -> user ();
数据库集成
向 users 表添加用于社交登录的列。
// database/migrations/xxxx_xx_xx_add_github_columns_to_users_table.php
return new class extends Migration
{
public function up () : void
{
Schema :: table ( 'users' , function ( Blueprint $table ) {
$table -> string ( 'github_id' ) -> nullable () -> unique () -> after ( 'id' );
$table -> string ( 'github_token' ) -> nullable () -> after ( 'github_id' );
$table -> string ( 'github_refresh_token' ) -> nullable () -> after ( 'github_token' );
});
}
public function down () : void
{
Schema :: table ( 'users' , function ( Blueprint $table ) {
$table -> dropColumn ([ 'github_id' , 'github_token' , 'github_refresh_token' ]);
});
}
};
使用 provider 列支持多个提供方
若要统一管理多个提供方,通常使用 provider / provider_id 两列。
Schema :: table ( 'users' , function ( Blueprint $table ) {
$table -> string ( 'provider' ) -> nullable () -> after ( 'id' );
$table -> string ( 'provider_id' ) -> nullable () -> after ( 'provider' );
$table -> string ( 'provider_token' ) -> nullable () -> after ( 'provider_id' );
$table -> unique ([ 'provider' , 'provider_id' ]);
});
回调处理时动态传入提供方名称。
Route :: get ( '/auth/{provider}/callback' , function ( string $provider ) {
$socialUser = Socialite :: driver ( $provider ) -> user ();
$user = User :: updateOrCreate (
[
'provider' => $provider ,
'provider_id' => $socialUser -> getId (),
],
[
'name' => $socialUser -> getName (),
'email' => $socialUser -> getEmail (),
'provider_token' => $socialUser -> token ,
]
);
Auth :: login ( $user );
return redirect ( '/dashboard' );
});
与已有用户关联
若要将账户与拥有同邮箱的已有用户关联,可以先按邮箱查找,再更新对应列。
$socialUser = Socialite :: driver ( 'github' ) -> user ();
$user = User :: where ( 'email' , $socialUser -> getEmail ()) -> first ();
if ( $user ) {
// 将 GitHub 信息关联到已有用户
$user -> update ([
'github_id' => $socialUser -> getId (),
'github_token' => $socialUser -> token ,
]);
} else {
// 作为新用户创建
$user = User :: create ([
'name' => $socialUser -> getName (),
'email' => $socialUser -> getEmail (),
'github_id' => $socialUser -> getId (),
'github_token' => $socialUser -> token ,
]);
}
Auth :: login ( $user );
作用域与选项
添加作用域
使用 scopes() 方法可以追加作用域。
return Socialite :: driver ( 'github' )
-> scopes ([ 'read:user' , 'public_repo' ])
-> redirect ();
setScopes() 会覆盖原有的所有作用域。
return Socialite :: driver ( 'github' )
-> setScopes ([ 'read:user' , 'public_repo' ])
-> redirect ();
可选参数
使用 with() 方法可以在重定向请求中附加额外参数。
// 在 Google 中限制托管域
return Socialite :: driver ( 'google' )
-> with ([ 'hd' => 'example.com' ])
-> redirect ();
// 在 Google 中每次都显示授权页面
return Socialite :: driver ( 'google' )
-> with ([ 'prompt' => 'consent' ])
-> redirect ();
请勿通过 with() 传递 state、response_type 等保留关键字。
Slack Bot 令牌
要生成 Slack Bot 令牌,可以使用 asBotUser()。
// 重定向时
return Socialite :: driver ( 'slack' )
-> asBotUser ()
-> setScopes ([ 'chat:write' , 'chat:write.public' , 'chat:write.customize' ])
-> redirect ();
// 回调时
$user = Socialite :: driver ( 'slack' ) -> asBotUser () -> user ();
Socialite 提供了 mock 机制,可以在不真的调用提供方接口的情况下测试 OAuth 流程。
测试重定向
use Laravel\Socialite\Facades\ Socialite ;
test ( '用户被重定向到 GitHub' , function () {
Socialite :: fake ( 'github' );
$response = $this -> get ( '/auth/github' );
$response -> assertRedirect ();
});
测试回调
向 fake() 传入用户实例,即可模拟从提供方返回的用户信息。可以用 User::fake() 生成 fake 用户。
use Laravel\Socialite\Facades\ Socialite ;
use Laravel\Socialite\Two\ User ;
test ( '可以通过 GitHub 登录' , function () {
Socialite :: fake ( 'github' , User :: fake ([
'id' => 'github-123' ,
'name' => 'Jane Doe' ,
'email' => '[email protected] ' ,
]));
$response = $this -> get ( '/auth/github/callback' );
$response -> assertRedirect ( '/dashboard' );
$this -> assertDatabaseHas ( 'users' , [
'name' => 'Jane Doe' ,
'email' => '[email protected] ' ,
'github_id' => 'github-123' ,
]);
});
默认会设置 fake 的 OAuth 令牌,如需覆盖可在 fake() 中传入更多属性。
$fakeUser = User :: fake ([
'id' => 'github-123' ,
'name' => 'Jane Doe' ,
'email' => '[email protected] ' ,
'token' => 'fake-token' ,
'refreshToken' => 'fake-refresh-token' ,
'expiresIn' => 3600 ,
'approvedScopes' => [ 'read:user' , 'public_repo' ],
]);
如果要伪造 OAuth 1 用户,请使用 Laravel\Socialite\One\User 类。
自定义提供方
如果内置提供方无法满足需求,官方推荐的做法是使用 Socialite::extend() 注册自定义驱动。SocialiteManager 继承自 Illuminate\Support\Manager,可以与其他 Laravel 驱动系统采用相同的方式进行扩展。
1. 创建提供方类
继承 Laravel\Socialite\Two\AbstractProvider,实现 4 个抽象方法。
// app/Socialite/ExampleProvider.php
namespace App\Socialite ;
use Laravel\Socialite\Two\ AbstractProvider ;
use Laravel\Socialite\Two\ User ;
class ExampleProvider extends AbstractProvider
{
// 授权 URL(用户被重定向到的地方)
public function getAuthUrl ( $state ) : string
{
return $this -> buildAuthUrlFromBase ( 'https://example.com/oauth/authorize' , $state );
}
// 获取访问令牌的 URL
protected function getTokenUrl () : string
{
return 'https://example.com/oauth/token' ;
}
// 使用访问令牌获取用户信息
protected function getUserByToken ( $token ) : array
{
$response = $this -> getHttpClient () -> get ( 'https://example.com/api/user' , [
'headers' => [ 'Authorization' => 'Bearer ' . $token ],
]);
return json_decode ( $response -> getBody (), true );
}
// 将数组映射为 Socialite 的 User 对象
protected function mapUserToObject ( array $user ) : User
{
return ( new User ) -> setRaw ( $user ) -> map ([
'id' => $user [ 'id' ],
'nickname' => $user [ 'login' ] ?? null ,
'name' => $user [ 'name' ],
'email' => $user [ 'email' ],
'avatar' => $user [ 'avatar_url' ] ?? null ,
]);
}
}
需要实现的 4 个方法的作用如下。
方法 作用 getAuthUrl($state)返回将用户重定向到的 OAuth 授权 URL getTokenUrl()将授权码换取为访问令牌的端点 getUserByToken($token)使用访问令牌调用用户信息 API,并以数组返回 mapUserToObject(array $user)将数组转换为 Socialite 的 User 对象
2. 在服务提供者中注册
在 AppServiceProvider 的 boot() 中通过 Socialite::extend() 注册驱动。
// app/Providers/AppServiceProvider.php
use App\Socialite\ ExampleProvider ;
use Laravel\Socialite\Facades\ Socialite ;
public function boot () : void
{
Socialite :: extend ( 'example' , function ( $app ) {
$config = $app [ 'config' ][ 'services.example' ];
return Socialite :: buildProvider ( ExampleProvider :: class , $config );
});
}
3. 添加配置
// config/services.php
'example' => [
'client_id' => env ( 'EXAMPLE_CLIENT_ID' ),
'client_secret' => env ( 'EXAMPLE_CLIENT_SECRET' ),
'redirect' => env ( 'EXAMPLE_REDIRECT_URI' ),
],
4. 像普通 Socialite 一样使用
注册完成后就可以像内置提供方一样使用完全相同的 API。
// 重定向
Route :: get ( '/auth/example' , function () {
return Socialite :: driver ( 'example' ) -> redirect ();
});
// 回调
Route :: get ( '/auth/example/callback' , function () {
$user = Socialite :: driver ( 'example' ) -> user ();
});
使用第三方通过 extend() 正规扩展 Socialite 的包也是不错的选择。
相关包
以下是本站作者维护的 Socialite 扩展包。它们都通过 Socialite::extend() 的规范方式实现,只需在 config/services.php 中加入配置即可使用。
LINE LINE SDK for Laravel。除 Socialite 的 OAuth 登录外,还集成了 Messaging API。
Bluesky 与 AT Protocol(Bluesky)集成,支持 OAuth 认证与发送 Post。
Discord Discord OAuth2 登录。
Threads 与 Meta Threads 集成,支持 OAuth 认证与发帖 API。
Amazon 通过 Login with Amazon 实现 OAuth 登录。
Mastodon 连接到 Mastodon 实例的 OAuth 登录。
WordPress WordPress.com 或自托管 WordPress 的 OAuth 登录。