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

相关推荐
北城笑笑44 分钟前
Server 18 ,Nginx 双服务器 HTTP转HTTPS 代理实战( HTTP 地址换 HTTPS 域名 )
运维·服务器·前端·后端·nginx·http·https
紫洋葱hh44 分钟前
Go 入门基础:类型、变量与运算符
开发语言·后端·ai·golang
打工仔折腾 AI1 小时前
Qt网络编程实战:UDP、TCP到HTTP的落地经验与选型
网络·人工智能·后端·python·qt·udp·ai agent 实战
打工仔折腾 AI1 小时前
SQL 编辑器不是高级记事本:KStudio 与 KSQL Developer 的建表与增删改查手法
数据库·人工智能·后端·python·sql·编辑器·ai agent 实战
拖孩2 小时前
# AI 帮我写的小程序,上线不到一个月流量主突然爆了
前端·后端·微信小程序
考虑考虑2 小时前
JFROG手动上传包
运维·后端·自动化运维
Java爱好狂.3 小时前
挑战一周速通Spring+SpringBoot+SpringCloud!
java·后端·spring·springboot·架构师·java面试·java程序员
柠檬味拥抱3 小时前
基于 YOLO11 的 18 类户外生活垃圾智能检测系统:从模型到 Streamlit 全流程实战
后端
得物技术3 小时前
动态规则的 Agent 实践:用确定性工程驾驭 LLM 的资产智能验收
人工智能·后端·llm
柠檬味拥抱3 小时前
基于 YOLO11 的低空空域反无人机预警系统:红外热成像 + Streamlit 全流程实战
后端