本文目录导读:

- 安装依赖包
- 配置文件 config/services.php
- .env 配置
- 创建数据表迁移
- 创建模型
- 创建服务类
- 创建控制器
- 路由配置
- Blade 视图示例
- 扩展:自定义QQ驱动
- 配置文件补充(如需自定义驱动)
- 安全建议
我来帮你实现Laravel项目的第三方登录多驱动功能,以下是完整的实现方案:
安装依赖包
composer require laravel/socialite
配置文件 config/services.php
<?php
return [
// ... 其他配置
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'redirect' => env('GITHUB_REDIRECT_URI'),
],
'wechat' => [
'client_id' => env('WECHAT_CLIENT_ID'),
'client_secret' => env('WECHAT_CLIENT_SECRET'),
'redirect' => env('WECHAT_REDIRECT_URI'),
],
'qq' => [
'client_id' => env('QQ_CLIENT_ID'),
'client_secret' => env('QQ_CLIENT_SECRET'),
'redirect' => env('QQ_REDIRECT_URI'),
],
'weibo' => [
'client_id' => env('WEIBO_CLIENT_ID'),
'client_secret' => env('WEIBO_CLIENT_SECRET'),
'redirect' => env('WEIBO_REDIRECT_URI'),
],
];
.env 配置
# GitHub GITHUB_CLIENT_ID=your_github_client_id GITHUB_CLIENT_SECRET=your_github_client_secret GITHUB_REDIRECT_URI=http://localhost:8000/auth/github/callback # 微信 WECHAT_CLIENT_ID=your_wechat_appid WECHAT_CLIENT_SECRET=your_wechat_secret WECHAT_REDIRECT_URI=http://localhost:8000/auth/wechat/callback # QQ QQ_CLIENT_ID=your_qq_appid QQ_CLIENT_SECRET=your_qq_appkey QQ_REDIRECT_URI=http://localhost:8000/auth/qq/callback # 微博 WEIBO_CLIENT_ID=your_weibo_appkey WEIBO_CLIENT_SECRET=your_weibo_secret WEIBO_REDIRECT_URI=http://localhost:8000/auth/weibo/callback
创建数据表迁移
// database/migrations/xxxx_create_oauth_logins_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateOauthLoginsTable extends Migration
{
public function up()
{
Schema::create('oauth_logins', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id')->nullable();
$table->string('provider'); // github, wechat, qq, weibo
$table->string('provider_user_id'); // 第三方用户ID
$table->string('nickname')->nullable();
$table->string('avatar')->nullable();
$table->string('email')->nullable();
$table->json('meta')->nullable(); // 额外的用户信息
$table->timestamps();
$table->unique(['provider', 'provider_user_id']);
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
public function down()
{
Schema::dropIfExists('oauth_logins');
}
}
创建模型
<?php
// app/Models/OauthLogin.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class OauthLogin extends Model
{
protected $fillable = [
'user_id', 'provider', 'provider_user_id',
'nickname', 'avatar', 'email', 'meta'
];
protected $casts = [
'meta' => 'array'
];
public function user()
{
return $this->belongsTo(User::class);
}
}
创建服务类
<?php
// app/Services/OAuthService.php
namespace App\Services;
use App\Models\User;
use App\Models\OauthLogin;
use Laravel\Socialite\Contracts\User as SocialUser;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
class OAuthService
{
/**
* 支持的第三方登录驱动
*/
protected $providers = [
'github' => 'github',
'wechat' => 'wechat',
'qq' => 'qq',
'weibo' => 'weibo',
];
/**
* 检查驱动是否支持
*/
public function providerExists($provider): bool
{
return array_key_exists($provider, $this->providers);
}
/**
* 获取支持的驱动列表
*/
public function getProviders(): array
{
return array_keys($this->providers);
}
/**
* 处理第三方登录
*/
public function handleOAuthCallback($provider, SocialUser $socialUser)
{
// 查找是否已绑定
$oauthLogin = OauthLogin::where('provider', $provider)
->where('provider_user_id', $socialUser->getId())
->first();
if ($oauthLogin && $oauthLogin->user) {
// 已绑定,直接登录
Auth::login($oauthLogin->user);
$oauthLogin->update([
'nickname' => $socialUser->getNickname() ?? $socialUser->getName(),
'avatar' => $socialUser->getAvatar(),
'email' => $socialUser->getEmail(),
'meta' => $this->getUserData($socialUser),
]);
return $oauthLogin->user;
}
// 未绑定,创建新用户或提示绑定
return $this->handleNewUser($provider, $socialUser);
}
/**
* 处理新用户
*/
protected function handleNewUser($provider, SocialUser $socialUser)
{
// 检查邮箱是否已存在
$email = $socialUser->getEmail();
if ($email) {
$user = User::where('email', $email)->first();
if ($user) {
// 邮箱已存在,自动绑定
$this->bindOAuthToUser($user, $provider, $socialUser);
Auth::login($user);
return $user;
}
}
// 创建新用户
$user = User::create([
'name' => $socialUser->getNickname() ?? $socialUser->getName() ?? '用户' . Str::random(6),
'email' => $email ?? $this->generateVirtualEmail($provider, $socialUser->getId()),
'password' => bcrypt(Str::random(32)),
'avatar' => $socialUser->getAvatar(),
'email_verified_at' => $email ? now() : null,
]);
$this->bindOAuthToUser($user, $provider, $socialUser);
Auth::login($user);
return $user;
}
/**
* 绑定第三方登录到用户
*/
public function bindOAuthToUser($user, $provider, SocialUser $socialUser)
{
return OauthLogin::updateOrCreate(
[
'provider' => $provider,
'provider_user_id' => $socialUser->getId(),
],
[
'user_id' => $user->id,
'nickname' => $socialUser->getNickname() ?? $socialUser->getName(),
'avatar' => $socialUser->getAvatar(),
'email' => $socialUser->getEmail(),
'meta' => $this->getUserData($socialUser),
]
);
}
/**
* 获取完整用户数据
*/
protected function getUserData(SocialUser $socialUser): array
{
$raw = $socialUser->getRaw();
return [
'raw' => $raw,
'token' => $socialUser->token,
'refresh_token' => $socialUser->refreshToken ?? null,
'expires_in' => $socialUser->expiresIn ?? null,
];
}
/**
* 生成虚拟邮箱
*/
protected function generateVirtualEmail($provider, $providerUserId): string
{
return "{$provider}-{$providerUserId}@oauth.local";
}
/**
* 解绑第三方登录
*/
public function unbindOAuth($user, $provider)
{
return OauthLogin::where('user_id', $user->id)
->where('provider', $provider)
->delete();
}
/**
* 获取用户绑定的第三方账号
*/
public function getUserOAuthLogins($user)
{
return OauthLogin::where('user_id', $user->id)->get();
}
}
创建控制器
<?php
// app/Http/Controllers/Auth/OAuthController.php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Services\OAuthService;
use Laravel\Socialite\Facades\Socialite;
use Illuminate\Http\Request;
class OAuthController extends Controller
{
protected $oauthService;
public function __construct(OAuthService $oauthService)
{
$this->oauthService = $oauthService;
}
/**
* 跳转到第三方登录页面
*/
public function redirect($provider)
{
if (!$this->oauthService->providerExists($provider)) {
abort(404, '不支持的第三方登录方式');
}
return Socialite::driver($provider)->redirect();
}
/**
* 处理第三方登录回调
*/
public function callback($provider)
{
if (!$this->oauthService->providerExists($provider)) {
abort(404, '不支持的第三方登录方式');
}
try {
$socialUser = Socialite::driver($provider)->user();
$user = $this->oauthService->handleOAuthCallback($provider, $socialUser);
// 登录成功后的处理
return redirect()->route('home')->with('success', '登录成功');
} catch (\Exception $e) {
\Log::error('OAuth登录失败', [
'provider' => $provider,
'error' => $e->getMessage()
]);
return redirect()->route('login')
->with('error', '第三方登录失败,请重试');
}
}
}
路由配置
<?php
// routes/web.php
use App\Http\Controllers\Auth\OAuthController;
// 第三方登录
Route::prefix('auth')->group(function () {
Route::get('{provider}', [OAuthController::class, 'redirect'])->name('oauth.redirect');
Route::get('{provider}/callback', [OAuthController::class, 'callback'])->name('oauth.callback');
});
Blade 视图示例
{{-- resources/views/auth/login.blade.php --}}
<div class="oauth-login">
<div class="oauth-title">其他登录方式</div>
@if($oauthLogins)
{{-- 显示已绑定的账号 --}}
<div class="bound-accounts">
@foreach($oauthLogins as $oauth)
<div class="account-item">
<span>{{ $oauth->provider }}: {{ $oauth->nickname }}</span>
<form action="{{ route('oauth.unbind', $oauth->provider) }}" method="POST">
@csrf
<button type="submit">解绑</button>
</form>
</div>
@endforeach
</div>
@endif
<div class="oauth-buttons">
<a href="{{ route('oauth.redirect', 'github') }}" class="btn btn-github">
<i class="fab fa-github"></i> GitHub 登录
</a>
<a href="{{ route('oauth.redirect', 'wechat') }}" class="btn btn-wechat">
<i class="fab fa-weixin"></i> 微信登录
</a>
<a href="{{ route('oauth.redirect', 'qq') }}" class="btn btn-qq">
<i class="fab fa-qq"></i> QQ 登录
</a>
<a href="{{ route('oauth.redirect', 'weibo') }}" class="btn btn-weibo">
<i class="fab fa-weibo"></i> 微博登录
</a>
</div>
</div>
扩展:自定义QQ驱动
<?php
// app/Providers/AppServiceProvider.php
use SocialiteProviders\Manager\SocialiteWasCalled;
use SocialiteProviders\QQ\QQExtendSocialite;
public function boot()
{
// 注册QQ驱动
$this->app->make(\SocialiteProviders\Manager\SocialiteWasCalled::class)
->extend('qq', QQExtendSocialite::class);
}
配置文件补充(如需自定义驱动)
<?php
// config/services.php 补充
'qq' => [
'client_id' => env('QQ_CLIENT_ID'),
'client_secret' => env('QQ_CLIENT_SECRET'),
'redirect' => env('QQ_REDIRECT_URI'),
],
安全建议
-
验证回调状态
$socialUser = Socialite::driver($provider)->stateless()->user();
-
使用 state 参数
return Socialite::driver($provider) ->with(['state' => csrf_token()]) ->redirect();
-
日志记录
\Log::info('第三方登录', [ 'provider' => $provider, 'user_id' => $user->id, 'ip' => request()->ip(), 'time' => now() ]);
这样你就完成了Laravel多驱动第三方登录的完整实现,记得根据实际需求调整代码,并确保在 .env 中正确配置所有第三方平台的App ID和密钥。