From e026602773daa59b3a1e519db772897e46d118cd Mon Sep 17 00:00:00 2001 From: Lu Fei <52o@qq52o.cn> Date: Tue, 17 Oct 2023 14:15:44 +0800 Subject: [PATCH] Support WeWork login (#26) * Support WeWork login * Update README --- README.md | 1 + composer.json | 14 +- src/WeWork/OAuth2.php | 257 +++++++++++++++++++++++++++++++++++++ src/WeWork/loginAgent.html | 101 +++++++++++++++ test/WeWork/callback.php | 16 +++ test/WeWork/common.php | 9 ++ test/WeWork/login.php | 16 +++ test/WeWork/loginAgent.php | 5 + test/WeWork/weblogin.php | 11 ++ 9 files changed, 428 insertions(+), 2 deletions(-) create mode 100644 src/WeWork/OAuth2.php create mode 100644 src/WeWork/loginAgent.html create mode 100644 test/WeWork/callback.php create mode 100644 test/WeWork/common.php create mode 100644 test/WeWork/login.php create mode 100644 test/WeWork/loginAgent.php create mode 100644 test/WeWork/weblogin.php diff --git a/README.md b/README.md index 6028085..e6df4df 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ API 文档:[https://apidoc.gitee.com/yurunsoft/YurunOAuthLogin](https://apidoc - GitLab - 飞书 - 钉钉 +- 企业微信 > 后续将不断添加新的平台支持,也欢迎你来提交PR,一起完善! diff --git a/composer.json b/composer.json index abbdc49..28d96a1 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "yurunsoft/yurun-oauth-login", - "description": "YurunOAuthLogin是一个PHP 第三方登录授权 SDK,集成了QQ、微信、微博、Github等常用接口。", + "description": "YurunOAuthLogin 是一个 PHP 第三方登录授权 SDK,集成了 QQ、微信、微博、GitHub 等常用接口。", "type": "library", "license": "MIT", "autoload": { @@ -8,6 +8,16 @@ "Yurun\\OAuthLogin\\": "src/" } }, + "authors": [ + { + "name": "Yurun", + "email": "admin@yurunsoft.com" + }, + { + "name": "Lu Fei", + "email": "lufei@simps.io" + } + ], "require": { "php": ">=5.5", "yurunsoft/yurun-http" : "~4.0|~5.0" @@ -15,4 +25,4 @@ "require-dev": { "friendsofphp/php-cs-fixer": "2.18.3" } -} \ No newline at end of file +} diff --git a/src/WeWork/OAuth2.php b/src/WeWork/OAuth2.php new file mode 100644 index 0000000..d115ac2 --- /dev/null +++ b/src/WeWork/OAuth2.php @@ -0,0 +1,257 @@ +agentid = $agentid; + } + + /** + * 获取登录授权url地址 + * + * @param string $name 跟在域名后的文本 + * @param array $params GET参数 + * + * @return string + */ + public function getAuthLoginUrl($name, $params = []) + { + return static::OPEN_DOMAIN . $name . (empty($params) ? '' : ('?' . $this->http_build_query($params))); + } + + /** + * 获取Web登录授权url地址 + * + * @param string $name 跟在域名后的文本 + * @param array $params GET参数 + * + * @return string + */ + public function getWebLoginUrl($name, $params = []) + { + return static::LOGIN_DOMAIN . $name . (empty($params) ? '' : ('?' . $this->http_build_query($params))); + } + + /** + * 获取url地址 + * + * @param string $name 跟在域名后的文本 + * @param array $params GET参数 + * + * @return string + */ + public function getUrl($name, $params = []) + { + return static::API_DOMAIN . $name . (empty($params) ? '' : ('?' . $this->http_build_query($params))); + } + + /** + * 第一步:获取登录页面跳转url. + * + * @param string $callbackUrl 登录回调地址 + * @param string $state 状态值,不传则自动生成,随后可以通过->state获取。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。一般为每个用户登录时随机生成state存在session中,登录回调中判断state是否和session中相同 + * @param array $scope 请求用户授权时向用户显示的可进行授权的列表。可空 + * @link https://developer.work.weixin.qq.com/document/path/91022 + * + * @return string + */ + public function getAuthUrl($callbackUrl = null, $state = null, $scope = null) + { + $option = [ + 'appid' => $this->appid, + 'agentid' => $this->agentid, + 'redirect_uri' => null === $callbackUrl ? $this->callbackUrl : $callbackUrl, + 'scope' => null === $scope ? $this->scope : $scope, + 'state' => $this->getState($state), + 'response_type' => 'code', + ]; + if (null === $this->loginAgentUrl) + { + return $this->getAuthLoginUrl('connect/oauth2/authorize', $option); + } + else + { + return $this->loginAgentUrl . '?' . $this->http_build_query($option); + } + } + + /** + * 第一步:生成企业微信扫码登录页面,获取登录页面跳转url. + * + * @param string $callbackUrl + * @param string $state + * @param string $loginType + * @link https://developer.work.weixin.qq.com/document/path/98152 + * + * @return string + */ + public function getWebAuthUrl($callbackUrl = null, $state = null, $loginType = 'CorpApp') + { + $option = [ + 'appid' => $this->appid, + 'agentid' => $this->agentid, + 'login_type' => $loginType, + 'redirect_uri' => null === $callbackUrl ? $this->callbackUrl : $callbackUrl, + 'state' => $this->getState($state) + ]; + if (null === $this->loginAgentUrl) + { + return $this->getWebLoginUrl('wwlogin/sso/login', $option); + } + else + { + return $this->loginAgentUrl . '?' . $this->http_build_query($option); + } + } + + /** + * 第二步:处理回调并获取access_token。与getAccessToken不同的是会验证state值是否匹配,防止csrf攻击。 + * + * @param string $storeState 存储的正确的state + * @param string $code 第一步里$redirectUri地址中传过来的code,为null则通过get参数获取 + * @param string $state 回调接收到的state,为null则通过get参数获取 + * + * @return string + */ + protected function __getAccessToken($storeState, $code = null, $state = null) + { + $this->result = $this->http->get($this->getUrl('cgi-bin/gettoken'), [ + 'corpid' => $this->appid, + 'corpsecret' => $this->appSecret + ])->json(true); + + if (!empty($this->result['errcode'])) + { + throw new ApiException($this->result['errmsg'], $this->result['errcode']); + } + else + { + return $this->accessToken = $this->result['access_token']; + } + } + + /** + * 获取用户资料. + * + * @param string $accessToken + * + * @return array + */ + public function getUserInfo($accessToken = null) + { + $token = null === $accessToken ? $this->accessToken : $accessToken; + $this->result = $this->http->get($this->getUrl('cgi-bin/auth/getuserinfo', [ + 'access_token' => $token, + 'code' => isset($_GET['code']) ? $_GET['code'] : '', + ])) + ->json(true); + + if (!empty($this->result['errcode'])) + { + throw new ApiException($this->result['errmsg'], $this->result['errcode']); + } + else + { + $this->openid = isset($this->result['userid']) ? $this->result['userid'] : $this->result['openid']; + + return $this->result; + } + } + + /** + * Get user detail info. + * + * @param string $userTicket + * @param string|null $accessToken + * @return array + */ + public function getUserDetail($userTicket, $accessToken = null) + { + $token = null === $accessToken ? $this->accessToken : $accessToken; + $this->result = $this->http->post($this->getUrl('cgi-bin/auth/getuserdetail', ['access_token' => $token]), [ + 'user_ticket' => $userTicket, + ], 'json') + ->json(true); + + if (!empty($this->result['errcode'])) + { + throw new ApiException($this->result['errmsg'], $this->result['errcode']); + } + else + { + $this->openid = $this->result['userid']; + + return $this->result; + } + } + + /** + * 刷新AccessToken续期 + * + * @param string $refreshToken + * + * @return bool + */ + public function refreshToken($refreshToken) + { + return false; + } + + /** + * 检验授权凭证AccessToken是否有效. + * + * @param string $accessToken + * + * @return bool + */ + public function validateAccessToken($accessToken = null) + { + try + { + $this->getUserInfo($accessToken); + + return true; + } + catch (ApiException $e) + { + return false; + } + } +} diff --git a/src/WeWork/loginAgent.html b/src/WeWork/loginAgent.html new file mode 100644 index 0000000..6d2d4b2 --- /dev/null +++ b/src/WeWork/loginAgent.html @@ -0,0 +1,101 @@ + + + + + + 企业微信登录 + + + + + + + diff --git a/test/WeWork/callback.php b/test/WeWork/callback.php new file mode 100644 index 0000000..592830b --- /dev/null +++ b/test/WeWork/callback.php @@ -0,0 +1,16 @@ +loginAgentUrl = 'http://test.com/test/WeWork/loginAgent.php'; + +var_dump( + 'access_token:', $weworkOAuth->getAccessToken($_SESSION['YURUN_WEWORK_STATE']), + '我也是access_token:', $weworkOAuth->accessToken, + '请求返回:', $weworkOAuth->result +); +var_dump( + '用户资料:', $weworkOAuth->getUserInfo(), + 'openid:', $weworkOAuth->openid +); diff --git a/test/WeWork/common.php b/test/WeWork/common.php new file mode 100644 index 0000000..5f03ff7 --- /dev/null +++ b/test/WeWork/common.php @@ -0,0 +1,9 @@ + '', + 'appkey' => '', + 'agentid' => '', + 'callbackUrl' => 'http://test.com/test/WeWork/callback.php', +]; diff --git a/test/WeWork/login.php b/test/WeWork/login.php new file mode 100644 index 0000000..f9275d4 --- /dev/null +++ b/test/WeWork/login.php @@ -0,0 +1,16 @@ +loginAgentUrl = 'http://test.com/test/WeWork/loginAgent.php'; + +// 所有为null的可不传,这里为了演示和加注释就写了 +$url = $weworkOAuth->getAuthUrl( + null, // 回调地址,登录成功后返回该地址 + null, // state 为空自动生成 + 'snsapi_privateinfo' // scope +); +$_SESSION['YURUN_WEWORK_STATE'] = $weworkOAuth->state; +header('location:' . $url); diff --git a/test/WeWork/loginAgent.php b/test/WeWork/loginAgent.php new file mode 100644 index 0000000..4e4249d --- /dev/null +++ b/test/WeWork/loginAgent.php @@ -0,0 +1,5 @@ +displayLoginAgent(); diff --git a/test/WeWork/weblogin.php b/test/WeWork/weblogin.php new file mode 100644 index 0000000..df8ef8b --- /dev/null +++ b/test/WeWork/weblogin.php @@ -0,0 +1,11 @@ +loginAgentUrl = 'http://test.com/test/WeWork/loginAgent.php'; + +$url = $weworkOAuth->getWebAuthUrl(); +$_SESSION['YURUN_WEWORK_STATE'] = $weworkOAuth->state; +header('location:' . $url);