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 是标准协议,各平台流程基本一致,只是参数略有不同。

相关推荐
神奇小汤圆2 小时前
Easy Desensitize:Java 高性能脱敏引擎的试用与实测
后端
追光者19952 小时前
Go micro集成DTM分布式事务的方法
后端
Turnip12022 小时前
深度解析:为什么简单的数据库"写操作"会在 MySQL 中卡住?
后端·mysql
神奇小汤圆2 小时前
终于找到一个好用的 Nginx 日志分析工具了!
后端
Java编程爱好者2 小时前
Condition底层机制剖析:多线程等待与通知机制
后端
Java编程爱好者3 小时前
面试官:你知道 MCP、Skill、Function Call 这三个的区别吗?
后端
狂炫冰美式3 小时前
把手从键盘上抬起来:AI 编程的 3 个不可逆阶段
前端·后端·ai编程
王中阳Go3 小时前
Go 协程池满了怎么办?面试官问我“兜底策略”,我差点挂了……
后端
蝎子莱莱爱打怪4 小时前
Centos7中一键安装K8s集群以及Rancher安装记录
运维·后端·kubernetes