OAuth2.0 第三方登录

接入微信、GitHub、Google 等第三方登录。

OAuth2.0 流程 用户 → 点击第三方登录 → 跳转授权页 → 用户授权 → 回调带 code ↓ code 换 access_token → 获取用户信息

GitHub 登录

php 复制代码
<?php  
class GitHubOAuth  
{  
    private string $clientId;  
    private string $clientSecret;  
    private string $redirectUri;  
      
    // 生成授权链接  
    public function getAuthUrl(string $state = ''): string  
    {  
        $params = http_build_query([  
            'client_id' => $this->clientId,  
            'redirect_uri' => $this->redirectUri,  
            'scope' => 'user:email',  
            'state' => $state ?: Str::random(16)  
        ]);  
          
        return "https://github.com/login/oauth/authorize?{$params}";  
    }  
      
    // code 换 token  
    public function getAccessToken(string $code): string  
    {  
        $response = Http::post('https://github.com/login/oauth/access_token', [  
            'client_id' => $this->clientId,  
            'client_secret' => $this->clientSecret,  
            'code' => $code  
        ])->body();  
          
        parse_str($response, $data);  
          
        if (!isset($data['access_token'])) {  
            throw new Exception('获取 access_token 失败');  
        }  
          
        return $data['access_token'];  
    }  
      
    // 获取用户信息  
    public function getUserInfo(string $accessToken): array  
    {  
        $response = Http::withHeaders([  
            'Authorization' => "Bearer {$accessToken}"  
        ])->get('https://api.github.com/user')->json();  
          
        return [  
            'id' => $response['id'],  
            'username' => $response['login'],  
            'nickname' => $response['name'],  
            'avatar' => $response['avatar_url'],  
            'email' => $response['email']  
        ];  
    }  
}

微信登录(开放平台)

php 复制代码
<?php  
class WechatOAuth  
{  
    // 生成授权链接(PC 扫码)  
    public function getAuthUrl(string $state = ''): string  
    {  
        $params = http_build_query([  
            'appid' => $this->appId,  
            'redirect_uri' => urlencode($this->redirectUri),  
            'response_type' => 'code',  
            'scope' => 'snsapi_login',  
            'state' => $state  
        ]);  
          
        return "https://open.weixin.qq.com/connect/qrconnect?{$params}[#wechat]()_redirect";  
    }  
      
    // code 换 token  
    public function getAccessToken(string $code): array  
    {  
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appId}&secret={$this->appSecret}&code={$code}&grant_type=authorization_code";  
          
        return Http::get($url)->json();  
    }  
      
    // 获取用户信息  
    public function getUserInfo(string $accessToken, string $openid): array  
    {  
        $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$accessToken}&openid={$openid}";  
          
        $response = Http::get($url)->json();  
          
        return [  
            'openid' => $response['openid'],  
            'unionid' => $response['unionid'] ?? null,  
            'nickname' => $response['nickname'],  
            'avatar' => $response['headimgurl']  
        ];  
    }  
}

统一封装

php 复制代码
<?php  
interface OAuthProvider  
{  
    public function getAuthUrl(string $state = ''): string;  
    public function getAccessToken(string $code): string;  
    public function getUserInfo(string $accessToken): array;  
}  
  
class OAuthService  
{  
    private array $providers = [];  
      
    public function __construct()  
    {  
        $this->providers = [  
            'github' => new GitHubOAuth(config('oauth.github')),  
            'wechat' => new WechatOAuth(config('oauth.wechat')),  
            'google' => new GoogleOAuth(config('oauth.google'))  
        ];  
    }  
      
    public function redirect(string $provider): RedirectResponse  
    {  
        $state = Str::random(16);  
        session(['oauth_state' => $state]);  
          
        return redirect($this->providers[$provider]->getAuthUrl($state));  
    }  
      
    public function callback(string $provider, string $code, string $state): User  
    {  
        // 验证 state  
        if ($state !== session('oauth_state')) {  
            throw new Exception('Invalid state');  
        }  
          
        $oauth = $this->providers[$provider];  
        $accessToken = $oauth->getAccessToken($code);  
        $oauthUser = $oauth->getUserInfo($accessToken);  
          
        // 查找或创建用户  
        $socialAccount = SocialAccount::firstOrCreate([  
            'provider' => $provider,  
            'provider_id' => $oauthUser['id']  
        ], [  
            'nickname' => $oauthUser['nickname'],  
            'avatar' => $oauthUser['avatar']  
        ]);  
          
        if (!$socialAccount->user_id) {  
            // 创建新用户  
            $user = User::create([  
                'nickname' => $oauthUser['nickname'],  
                'avatar' => $oauthUser['avatar']  
            ]);  
            $socialAccount->update(['user_id' => $user->id]);  
        }  
          
        return $socialAccount->user;  
    }  
}

数据库设计

sql 复制代码
CREATE TABLE social_accounts (  
    id BIGINT PRIMARY KEY AUTO_INCREMENT,  
    user_id BIGINT NULL,  
    provider VARCHAR(20),  
    provider_id VARCHAR(100),  
    nickname VARCHAR(100),  
    avatar VARCHAR(255),  
    access_token VARCHAR(255),  
    refresh_token VARCHAR(255),  
    expires_at TIMESTAMP NULL,  
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,  
    UNIQUE KEY uk_provider (provider, provider_id),  
    INDEX idx_user (user_id)  
);

控制器

php 复制代码
<?php  
class OAuthController  
{  
    public function redirect(string $provider)  
    {  
        return $this->oauthService->redirect($provider);  
    }  
      
    public function callback(Request $request, string $provider)  
    {  
        $user = $this->oauthService->callback(  
            $provider,  
            $request->input('code'),  
            $request->input('state')  
        );  
          
        Auth::login($user);  
          
        return redirect('/');  
    }  
}  
  
// routes/web.php  
Route::get('/oauth/{provider}', [OAuthController::class, 'redirect']);  
Route::get('/oauth/{provider}/callback', [OAuthController::class, 'callback']);

总结

平台 授权域名
GitHub github.com/login/oauth
微信 open.weixin.qq.com
Google accounts.google.com
QQ graph.qq.com

OAuth2.0 是标准协议,各平台流程基本一致,只是参数略有不同。

相关推荐
阳光是sunny几秒前
别再被 worktree 绕晕了!AI 编程时代你必须掌握的 Git 隔离神器
前端·人工智能·后端
万少1 小时前
万少的博客 - 技术分享与解决方案
前端·javascript·后端
咖啡八杯1 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式
苍何1 小时前
腾讯再放大招,企微 Agent 大圆开启内测
后端
ethantan2 小时前
一篇讲解AI Agent 组成:像人一样思考的智能体
人工智能·后端·程序员
Cosolar4 小时前
vLLM 生产级部署完全指南
人工智能·后端·架构
IT_陈寒4 小时前
垃圾回收器选错了,我的Java服务内存炸了
前端·人工智能·后端
用户8356290780515 小时前
使用 Python 在 PDF 中创建与管理书签
后端·python
Nturmoils5 小时前
字段太多看不全,ksql 的展开模式和输出控制怎么用
数据库·后端
大志说编程5 小时前
Agent面试真题06: 十分钟带你快速掌握Agent记忆管理高频面试题(附详细答案)
后端·面试·ai编程