webman-Socialite社会化认证(第三方登入)支持QQ/微信/微博/抖音/支付宝/淘宝等

TycoonSong

插件地址 https://www.workerman.net/plugin/108

示例中使用到的登入Auth插件 https://www.workerman.net/plugin/24

webman-socialite

webman-socialite 是一个 OAuth2 社会化认证工具。 它借鉴于 laravel/socialite ,你可以轻易的运行在webman的laravel/tp/yii等项目中使用它

'Latest Stable Version' 'Total Downloads' 'License'

该工具现已支持平台有:Facebook,Github,Google,Linkedin,Outlook,QQ,TAPD,支付宝,淘宝,百度,钉钉,微博,微信,抖音,飞书,Lark,豆瓣,企业微信,腾讯云,Line,Gitee,Coding。

如果你觉得方便了你,可以为我点个小星星点击这里 :heart:

安装

composer require shopwwi/webman-socialite

配置

在使用 Socialite 之前,您还需要为应用程序使用的 OAuth 服务添加凭据。 这些凭证应该放在你的 config/plugin/shopwwi/socialite/app.php 配置文件中,并且应该使用密钥 facebook,twitter,linkedin,google,github,gitlab 或 bitbucket, 取决于您的应用程序所需的提供商。 例如:

   'driver' => [
      ...
      'qq' => [
          'provider' => \Shopwwi\WebmanSocialite\Providers\QqProvider::class,
          'client_id' => '',
          'client_secret' => '',
          'redirect' => 'http://your-callback-url',
      ],
      ...
   ]

使用方法

接下来,就要对用户认证了!这需要两个路由:一个路由用于把用户重定向到 OAuth provider,另一个则用于在认证完成后接收相应 provider 的回调请求。可以通过 Socialite facade 的方式来访问 Socialite:

<?php

namespace app\controller\auth;

use support\Request;
use Shopwwi\WebmanSocialite\Facade\Socialite;

class QqController
{
    /**
     * 将用户重定向到 QQ 的授权页面
     *
     * @return 
     */
    public function redirect(Request $request)
    {
        $redirect = Socialite::driver('qq')->redirect();
        return redirect($redirect);
    }

    /**
     * 从 QQ 获取用户信息
     *
     */
    public function callback(Request $request)
    {
         $code = $request->input('code');

         $qqUser = Socialite::driver('qq')->userFromCode($code);

         //示例
         $user = User::where('qq_id', $qqUser->id)->first();
         if ($user) {
           $user->update([
               'qq_token' => $qqUser->access_token,
               'qq_refresh_token' => $qqUser->refresh_token,
           ]);
         } else {
           $user = User::create([
               'name' => $qqUser->name,
               'email' => $qqUser->email,
               'qq_id' => $qqUser->id,
               'qq_token' => $qqUser->access_token,
               'qq_refresh_token' => $qqUser->refresh_token,
           ]);
         }
          Auth::login($user);
          return redirect('/index');
    }
}

配置

扩展自定义服务提供程序

你可以很轻松的对socialite进行扩展来满足你不同的第三方登入需求

1.直接在config配置里添加你的应用驱动

'driver' => [
    ..
    'line' => [
        'provider' => \app\provider\LineProvider::class, 
        'client_id' => 'your-app-id',
        'client_secret' => 'your-app-secret',
        'redirect' => 'http://your-callback-url',
    ],
    ..
];

$socialite = Socialite::driver('line')->redirect();

2.使用闭包函数进行扩展

$config = [
    'line' =>[
        'provider' => 'line',
        'client_id' => 'your-app-id',
        'client_secret' => 'your-app-secret',
        'redirect' => 'http://your-callback-url',
    ]   
];
$socialite = Socialite::config(new \Shopwwi\WebmanSocialite\Config($config))->extend('line', function(array $config) {
    return new LineProvider($config);
})->driver('line')->redirect();

// 下面直接注入也是可以的哈
$config = [
    'line' =>[
        'provider' => \app\provider\LineProvider::class,
        'client_id' => 'your-app-id',
        'client_secret' => 'your-app-secret',
        'redirect' => 'http://your-callback-url',
    ]   
];
$socialite = Socialite::config(new \Shopwwi\WebmanSocialite\Config($config))->driver('line')->redirect();

3.接下来为 AppleProvider 设置实际操作方法:

你的自定义服务提供类必须实现\Shopwwi\WebmanSocialite\Contracts\Provider 接口

    namespace app\provider;
    class LineProvider implements \Shopwwi\WebmanSocialite\Contracts\Provider
    {
        //...
    }

下面的示例继承了 \Shopwwi\WebmanSocialite\Providers\AbstractProvider 大多数逻辑都是实现好了的 微调即可

    namespace app\provider;
    use Shopwwi\WebmanSocialite\Providers\AbstractProvider;
    use Shopwwi\WebmanSocialite\Contracts;
    use Shopwwi\WebmanSocialite\AbstractUser;
    class LineProvider extends AbstractProvider
    {
        public const NAME = 'line';

        protected string $baseUrl = 'https://api.line.me/oauth2/';

        protected string $version = 'v2.1';

        protected array $scopes = ['profile'];

        protected function getAuthUrl(): string
        {
            $this->state = $this->state ?: \md5(\uniqid(Contracts\SHOPWWI_SOC_STATE, true));

            return $this->buildAuthUrlFromBase('https://access.line.me/oauth2/'.$this->version.'/authorize');
        }

        protected function getTokenUrl(): string
        {
            return $this->baseUrl.$this->version.'/token';
        }

        /**
         * @param string $code
         * @return array
         */
        protected function getTokenFields(string $code): array
        {
            return parent::getTokenFields($code) + [Contracts\SHOPWWI_SOC_GRANT_TYPE => Contracts\SHOPWWI_SOC_AUTHORIZATION_CODE];
        }

        /**
         * @param string $token
         * @return array
         * @throws \GuzzleHttp\Exception\GuzzleException
         */
        protected function getUserByToken(string $token): array
        {
            $response = $this->getHttpClient()->get(
                'https://api.line.me/v2/profile',
                [
                    'headers' => [
                        'Accept' => 'application/json',
                        'Authorization' => 'Bearer '.$token,
                    ],
                ]
            );

            return $this->fromJsonBody($response);
        }

        /**
         * @param array $user
         * @return Contracts\User
         */
        protected function mapUserToObject(array $user): Contracts\User
        {
            return new AbstractUser([
                Contracts\SHOPWWI_SOC_ID => $user['userId'] ?? null,
                Contracts\SHOPWWI_SOC_NAME => $user['displayName'] ?? null,
                Contracts\SHOPWWI_SOC_NICKNAME => $user['displayName'] ?? null,
                Contracts\SHOPWWI_SOC_AVATAR => $user['pictureUrl'] ?? null,
                Contracts\SHOPWWI_SOC_EMAIL => null,
            ]);
        }
    }

平台

不同的平台有不同的配置方法,为了确保工具的正常运行,所以请确保你所使用的平台的配置都是如期设置的。

支付宝

请按如下方式配置

$code = \request()->input('code');
$user = Socialite::driver('alipay')->userFromCode($code);

// 详见文档后面 "User interface"
$user->getId();        
$user->getNickname();
$user->getUsername();
$user->getName();
...

钉钉

如文档所示

注意:该工具仅支持 QR code 连接到第三方网站,用来获取用户信息(openid, unionid 和 nickname)


$code = \request()->input('code');
$user = Socialite::driver('dingtalk')->userFromCode($code);

// 详见文档后面 "User interface"
$user->getId();       
$user->getNickname();  
$user->getUsername(); 
$user->getName(); 
...

抖音


$code = \request()->input('code');
$user = Socialite::driver('douyin')->userFromCode($code);
// 通过access_token获取用户信息时,需先设置openId
$openId = '4154d454..5561';
$token = '';
$user = Socialite::driver('douyin')->withOpenId($openId)->userFromToken($token);

头条

$code = \request()->input('code');
$user = Socialite::driver('toutiao')->userFromCode($code);
// 通过access_token获取用户信息时,需先设置openId
$openId = '4154d454..5561';
$token = '';
$user = Socialite::driver('toutiao')->withOpenId($openId)->userFromToken($token);

西瓜


$code = \request()->input('code');
$user = Socialite::driver('toutiao')->userFromCode($code);
//通过access_token获取用户信息时,需先设置openId
$openId = '4154d454..5561';
$token = '';
$user = Socialite::driver('toutiao')->withOpenId($openId)->userFromToken($token);

百度

其他配置没啥区别,在用法上,可以很轻易的选择重定向登录页面的模式,通过 withDisplay()

  • page:全屏形式的授权页面 (默认),适用于 web 应用。
  • popup: 弹框形式的授权页面,适用于桌面软件应用和 web 应用。
  • dialog: 浮层形式的授权页面,只能用于站内 web 应用。
  • mobile: Iphone/Android 等智能移动终端上用的授权页面,适用于 Iphone/Android 等智能移动终端上的应用。
  • tv: 电视等超大显示屏使用的授权页面。
  • pad: IPad/Android 等智能平板电脑使用的授权页面。
$authUrl = Socialite::driver('baidu')->withDisplay('mobile')->redirect();

popup 模式是工具内默认的使用模式。basic 是默认使用的 scopes 值。

飞书

通过一些简单的方法配置 app_ticket 就能使用内部应用模式


$code = \request()->input('code');
$user = Socialite::driver('feishu')->withInternalAppMode()->userFromCode($code);

$appTicket = '';
$code = \request()->input('code');
$user = Socialite::driver('feishu')->withDefaultMode()->withAppTicket($appTicket)->userFromCode($code);

Lark

通过一些简单的方法配置 app_ticket 就能使用内部应用模式


$code = \request()->input('code');
$user = Socialite::driver('lark')->withInternalAppMode()->userFromCode($code);

$appTicket = '';
$code = \request()->input('code');
$user = Socialite::driver('lark')->withDefaultMode()->withAppTicket($appTicket)->userFromCode($code);

淘宝

其他配置与其他平台的一样,你能选择你想要展示的重定向页面类型通过使用 withView()

$authUrl = Socialite::driver('taobao')->withView('wap')->redirect();

web 模式是工具默认使用的展示方式, user_info 是默认使用的 scopes 范围值。

微信

我们支持开放平台代表公众号进行第三方平台网页授权。

你只需要像下面这样输入你的配置。官方账号不需要授权。

     'wechat' => [
         'provider' => \Shopwwi\WebmanSocialite\Providers\WeChatProvider::class,
         'client_id' => 'your-app-id',
         'client_secret' => 'your-app-secret',
         'redirect' => 'http://your-callback-url',
         // 开放平台 - 第三方平台所需
         'component' => [
             // or 'app_id', 'component_app_id' as key
             'id' => 'component-app-id',
             // or 'app_token', 'access_token', 'component_access_token' as key
             'token' => 'component-access-token',
         ]
     ],

Coding

您需要额外配置 team_url 为您的团队域名

其他一些技巧

访问作用域

在重定向用户之前,你还可以使用 scopes 方法在请求中添加其他「作用域」。此方法将覆盖所有现有的作用域:


$redirect = Socialite::driver('qq')->scopes(['scope1', 'scope2'])->redirect();

回调链接

你也可以动态设置 redirect_uri ,你可以使用以下方法来改变 redirect_uri URL:

$url = 'your callback url.';

Socialite::driver('qq')->redirect($url);
// or
Socialite::driver('qq')->withRedirectUrl($url)->redirect();

State

你的应用程序可以使用一个状态参数来确保响应属于同一个用户发起的请求,从而防止跨站请求伪造 (CSFR) 攻击。当恶意攻击者欺骗用户执行不需要的操作 (只有用户有权在受信任的 web 应用程序上执行) 时,就会发生 CSFR 攻击,所有操作都将在不涉及或警告用户的情况下完成。

这里有一个最简单的例子,说明了如何提供状态可以让你的应用程序更安全。在本例中,我们使用会话 ID 作为状态参数,但是您可以使用您想要为状态创建值的任何逻辑。

带着 state 参数的重定向

<?php
session_start();

// Assign to state the hashing of the session ID
$state = hash('sha256', session_id());

$url = Socialite::driver('qq')->withState($state)->redirect();

return redirect($url); 

检验回调的 state

一旦用户授权你的应用程序,用户将被重定向回你的应用程序的 redirect_uri。OAuth 服务器将不加修改地返回状态参数。检查 redirect_uri 中提供的状态是否与应用程序生成的状态相匹配:

<?php
session_start();

$state = \request()->input('state');
$code = \request()->input('code');

// Check the state received with current session id
if ($state != hash('sha256', session_id())) {
    exit('State does not match!');
}
$user = Socialite::driver('qq')->userFromCode($code);

// authorized

查看更多关于 state 参数的文档

可选参数

许多 OAuth providers 支持重定向请求中的可选参数。 要在请求中包含任何可选参数,请使用关联数组调用 with 方法:

$response = Socialite::driver('qq')->with(['hd' => 'example.com'])->redirect();

返回用户信息

标准的 user api:

$user = Socialite::driver('qq')->userFromCode($code);

你可以像这样以数组键的形式获取 user 属性:

$user['id']; 
$user['nickname'];
$user['name']; 
$user['email'];
...

或者使用该 User 对象的方法:


$user->getId();
$user->getNickname();
$user->getName();
$user->getEmail();
$user->getAvatar();
$user->getRaw();
$user->getAccessToken(); 
$user->getRefreshToken();
$user->getExpiresIn();
$user->getTokenResponse();

从 OAuth API 响应中取得原始数据

$user->getRaw() 方法会返回一个 array

当你使用 userFromCode() 想要获取 token 响应的原始数据

$user->getTokenResponse() 方法会返回一个 array 里面是响应从获取 token 时候 API 返回的响应。

注意:当你使用 userFromCode() 时,这个方法只返回一个 有效的数组,否则将返回 null,因为 userFromToken() 没有 token 的 HTTP 响应。

通过 access token 获取用户信息

$accessToken = 'xxxxxxxxxxx';
$user = $socialite->userFromToken($accessToken);

参照

License

MIT

2088 4 5
4个评论

yzh52521

overtrue/socialite 非laravel 用这个挺好的 没必要从复制一份

  • TycoonSong 2023-02-16

    有包含它这个包的用法 主要是为了后续的便利 以及更适合webman的特性 向webman靠拢 以及兼容php7.X/8.X

wocall

看都没看完,太复杂了

  • 暂无评论
yanglinjun

根本没法用

  • 暂无评论
jenchih

github获取邮箱时,经常提示如下错误

Error: Undefined constant "Shopwwi\WebmanSocialite\Contracts\SHOPWWI_SOC_EMAIL" in /www/work/vendor/shopwwi/webman-socialite/src/Providers/GitHubProvider.php:65
Stack trace:

0 /www/work/vendor/shopwwi/webman-socialite/src/Providers/AbstractProvider.php(132): Shopwwi\WebmanSocialite\Providers\GitHubProvider->getUserByToken('gho_GtfQG28R4vR...')

1 /www/work/vendor/shopwwi/webman-socialite/src/Providers/AbstractProvider.php(119): Shopwwi\WebmanSocialite\Providers\AbstractProvider->userFromToken('gho_GtfQG28R4vR...')

2 /www/work/app/console/controller/EntranceController.php(562): Shopwwi\WebmanSocialite\Providers\AbstractProvider->userFromCode('3e1e6150539e2e0...')

3 /www/work/app/console/controller/EntranceController.php(607): app\console\controller\EntranceController->callbackByOauth('github', '3e1e6150539e2e0...')

4 /www/work/vendor/workerman/webman-framework/src/App.php(319): app\console\controller\EntranceController->callbackByGithub(Object(support\Request))

5 /www/work/vendor/workerman/webman-framework/src/App.php(349): Webman\App::Webman{closure}(Object(support\Request))

6 /www/work/app/console/middleware/Log.php(12): Webman\App::Webman{closure}(Object(support\Request))

7 /www/work/vendor/workerman/webman-framework/src/App.php(341): app\console\middleware\Log->process(Object(support\Request), Object(Closure))

8 /www/work/app/console/middleware/CheckPermission.php(43): Webman\App::Webman{closure}(Object(support\Request))

9 /www/work/vendor/workerman/webman-framework/src/App.php(341): app\console\middleware\CheckPermission->process(Object(support\Request), Object(Closure))

10 /www/work/app/console/middleware/Auth.php(52): Webman\App::Webman{closure}(Object(support\Request))

11 /www/work/vendor/workerman/webman-framework/src/App.php(341): app\console\middleware\Auth->process(Object(support\Request), Object(Closure))

12 /www/work/vendor/webman/log/src/Middleware.php(96): Webman\App::Webman{closure}(Object(support\Request))

13 /www/work/vendor/workerman/webman-framework/src/App.php(341): Webman\Log\Middleware->process(Object(support\Request), Object(Closure))

14 /www/work/vendor/yzh52521/webman-throttle/src/Throttle.php(145): Webman\App::Webman{closure}(Object(support\Request))

15 /www/work/app/middleware/Throttle.php(29): yzh52521\middleware\Throttle->handle(Object(support\Request), Object(Closure), Array)

16 /www/work/vendor/workerman/webman-framework/src/App.php(341): app\middleware\Throttle->process(Object(support\Request), Object(Closure))

17 /www/work/app/middleware/Init.php(24): Webman\App::Webman{closure}(Object(support\Request))

18 /www/work/vendor/workerman/webman-framework/src/App.php(341): app\middleware\Init->process(Object(support\Request), Object(Closure))

19 /www/work/vendor/workerman/webman-framework/src/App.php(168): Webman\App::Webman{closure}(Object(support\Request))

20 /www/work/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage(Object(Workerman\Connection\TcpConnection), Object(support\Request))

21 /www/work/vendor/workerman/workerman/Events/Select.php(311): Workerman\Connection\TcpConnection->baseRead(Resource id #274)

22 /www/work/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Select->loop()

23 /www/work/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux(Object(Workerman\Worker))

24 /www/work/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()

25 /www/work/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()

26 /www/work/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()

27 /www/work/start.php(4): support\App::run()

28 {main}

  • 暂无评论

TycoonSong

1224
积分
0
获赞数
0
粉丝数
2021-10-29 加入
×
🔝