PHP项目Laravel第三方登录多驱动

wen PHP项目 3

本文目录导读:

PHP项目Laravel第三方登录多驱动

  1. 安装依赖包
  2. 配置文件 config/services.php
  3. .env 配置
  4. 创建数据表迁移
  5. 创建模型
  6. 创建服务类
  7. 创建控制器
  8. 路由配置
  9. Blade 视图示例
  10. 扩展:自定义QQ驱动
  11. 配置文件补充(如需自定义驱动)
  12. 安全建议

我来帮你实现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'),
],

安全建议

  1. 验证回调状态

    $socialUser = Socialite::driver($provider)->stateless()->user();
  2. 使用 state 参数

    return Socialite::driver($provider)
     ->with(['state' => csrf_token()])
     ->redirect();
  3. 日志记录

    \Log::info('第三方登录', [
     'provider' => $provider,
     'user_id' => $user->id,
     'ip' => request()->ip(),
     'time' => now()
    ]);

这样你就完成了Laravel多驱动第三方登录的完整实现,记得根据实际需求调整代码,并确保在 .env 中正确配置所有第三方平台的App ID和密钥。

抱歉,评论功能暂时关闭!