插件地址 https://www.workerman.net/plugin/108
示例中使用到的登入Auth插件 https://www.workerman.net/plugin/24
webman-socialite 是一个 OAuth2 社会化认证工具。 它借鉴于 laravel/socialite ,你可以轻易的运行在webman的laravel/tp/yii等项目中使用它
该工具现已支持平台有: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()
$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);
通过一些简单的方法配置 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',
]
],
您需要额外配置 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();
你的应用程序可以使用一个状态参数来确保响应属于同一个用户发起的请求,从而防止跨站请求伪造 (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
许多 OAuth providers 支持重定向请求中的可选参数。 要在请求中包含任何可选参数,请使用关联数组调用 with
方法:
$response = Socialite::driver('qq')->with(['hd' => 'example.com'])->redirect();
$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();
$user->getRaw()
方法会返回一个 array。
$user->getTokenResponse()
方法会返回一个 array 里面是响应从获取 token 时候 API 返回的响应。
注意:当你使用
userFromCode()
时,这个方法只返回一个 有效的数组,否则将返回 null,因为userFromToken()
没有 token 的 HTTP 响应。
$accessToken = 'xxxxxxxxxxx';
$user = $socialite->userFromToken($accessToken);
MIT
overtrue/socialite 非laravel 用这个挺好的 没必要从复制一份
有包含它这个包的用法 主要是为了后续的便利 以及更适合webman的特性 向webman靠拢 以及兼容php7.X/8.X
看都没看完,太复杂了
根本没法用
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}