OAuth2.0 第三方登录

0 阅读1分钟

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

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

GitHub 登录

<?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  
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  
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;  
    }  
}

数据库设计

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  
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']);

总结

平台授权域名
GitHubgithub.com/login/oauth
微信open.weixin.qq.com
Googleaccounts.google.com
QQgraph.qq.com

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