|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* This file is part of the overtrue/socialite. |
|
5
|
|
|
* |
|
6
|
|
|
* (c) overtrue <[email protected]> |
|
7
|
|
|
* |
|
8
|
|
|
* This source file is subject to the MIT license that is bundled |
|
9
|
|
|
* with this source code in the file LICENSE. |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace Overtrue\Socialite\Providers; |
|
13
|
|
|
|
|
14
|
|
|
use Overtrue\Socialite\AccessToken; |
|
15
|
|
|
use Overtrue\Socialite\AccessTokenInterface; |
|
16
|
|
|
use Overtrue\Socialite\InvalidArgumentException; |
|
17
|
|
|
use Overtrue\Socialite\ProviderInterface; |
|
18
|
|
|
use Overtrue\Socialite\User; |
|
19
|
|
|
|
|
20
|
|
|
/** |
|
21
|
|
|
* Class WeChatProvider. |
|
22
|
|
|
* |
|
23
|
|
|
* @link http://mp.weixin.qq.com/wiki/9/01f711493b5a02f24b04365ac5d8fd95.html [WeChat - 公众平台OAuth文档] |
|
24
|
|
|
* @link https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN [网站应用微信登录开发指南] |
|
25
|
|
|
*/ |
|
26
|
|
|
class CorpWechatProvider extends AbstractProvider implements ProviderInterface |
|
27
|
|
|
{ |
|
28
|
|
|
/** |
|
29
|
|
|
* The base url of WeChat API. |
|
30
|
|
|
* |
|
31
|
|
|
* @var string |
|
32
|
|
|
*/ |
|
33
|
|
|
protected $user_baseinfo_api = 'https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo'; |
|
34
|
|
|
protected $user_info_api = 'https://qyapi.weixin.qq.com/cgi-bin/user/get'; |
|
35
|
|
|
protected $access_api_url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken'; |
|
36
|
|
|
|
|
37
|
|
|
/** |
|
38
|
|
|
* {@inheritdoc}. |
|
39
|
|
|
*/ |
|
40
|
|
|
protected $openId; |
|
41
|
|
|
|
|
42
|
|
|
/** |
|
43
|
|
|
* {@inheritdoc}. |
|
44
|
|
|
*/ |
|
45
|
|
|
protected $scopes = ['snsapi_base']; |
|
46
|
|
|
|
|
47
|
|
|
/** |
|
48
|
|
|
* Indicates if the session state should be utilized. |
|
49
|
|
|
* |
|
50
|
|
|
* @var bool |
|
51
|
|
|
*/ |
|
52
|
|
|
protected $stateless = true; |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* {@inheritdoc}. |
|
56
|
|
|
*/ |
|
57
|
|
|
protected function getAuthUrl($state) |
|
58
|
|
|
{ |
|
59
|
|
|
$path = 'oauth2/authorize'; |
|
60
|
|
|
|
|
61
|
|
|
$url = "https://open.weixin.qq.com/connect/{$path}"; |
|
62
|
|
|
// dd($url); |
|
|
|
|
|
|
63
|
|
|
return $this->buildAuthUrlFromBase($url, $state); |
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* {@inheritdoc}. |
|
68
|
|
|
*/ |
|
69
|
|
|
protected function buildAuthUrlFromBase($url, $state) |
|
70
|
|
|
{ |
|
71
|
|
|
$query = http_build_query($this->getCodeFields($state), '', '&', $this->encodingType); |
|
72
|
|
|
$url = $url.'?'.$query.'#wechat_redirect'; |
|
73
|
|
|
return $url; |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
/** |
|
77
|
|
|
* {@inheritdoc}. |
|
78
|
|
|
*/ |
|
79
|
|
View Code Duplication |
protected function getCodeFields($state = null) |
|
|
|
|
|
|
80
|
|
|
{ |
|
81
|
|
|
|
|
82
|
|
|
$result = array_merge([ |
|
83
|
|
|
'appid' => $this->clientId, |
|
84
|
|
|
'redirect_uri' => $this->redirectUrl, |
|
85
|
|
|
'response_type' => 'code', |
|
86
|
|
|
'scope' => $this->formatScopes($this->scopes, $this->scopeSeparator), |
|
87
|
|
|
'state' => $state ?: md5(time()), |
|
88
|
|
|
], $this->parameters); |
|
89
|
|
|
|
|
90
|
|
|
return $result; |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
/** |
|
94
|
|
|
* 获取 access token的路径. |
|
95
|
|
|
*/ |
|
96
|
|
|
protected function getTokenUrl() |
|
97
|
|
|
{ |
|
98
|
|
|
return $this->access_api_url; |
|
99
|
|
|
} |
|
100
|
|
|
|
|
101
|
|
|
/** |
|
102
|
|
|
* {@inheritdoc}. |
|
103
|
|
|
*/ |
|
104
|
|
|
protected function getUserByToken(AccessTokenInterface $token) |
|
105
|
|
|
{ |
|
106
|
|
|
|
|
107
|
|
|
|
|
108
|
|
|
if (empty($token['UserId'])) { |
|
109
|
|
|
throw new InvalidArgumentException('UserId of AccessToken is required.'); |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
$response = $this->getHttpClient()->get($this->user_info_api, [ |
|
113
|
|
|
'query' => [ |
|
114
|
|
|
'access_token' => $token->getToken(), |
|
115
|
|
|
'userid' => $token['UserId'], |
|
116
|
|
|
], |
|
117
|
|
|
]); |
|
118
|
|
|
|
|
119
|
|
|
return json_decode($response->getBody(), true); |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
/** |
|
123
|
|
|
* {@inheritdoc}. |
|
124
|
|
|
*/ |
|
125
|
|
|
protected function mapUserToObject(array $user) |
|
126
|
|
|
{ |
|
127
|
|
|
return new User([ |
|
128
|
|
|
'userid' => $this->arrayItem($user, 'userid'), |
|
129
|
|
|
'name' => $this->arrayItem($user, 'name'), |
|
130
|
|
|
'avatar' => $this->arrayItem($user, 'avatar'), |
|
131
|
|
|
'mobile' => $this->arrayItem($user, 'mobile'), |
|
132
|
|
|
'department' => $this->arrayItem($user, 'department'), |
|
133
|
|
|
'gender' => $this->arrayItem($user, 'gender'), |
|
134
|
|
|
'email' => $this->arrayItem($user, 'email'), |
|
135
|
|
|
'status' => $this->arrayItem($user, 'status'), |
|
136
|
|
|
]); |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
/** |
|
140
|
|
|
* 构建access_token 的参数列表, 分为两种情况一种是 获取access token, 另一种是直接获取userid |
|
141
|
|
|
*/ |
|
142
|
|
|
protected function getTokenFields($code = false) |
|
143
|
|
|
{ |
|
144
|
|
|
//参数列表: |
|
145
|
|
|
// https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=id&corpsecret=secrect |
|
146
|
|
|
|
|
147
|
|
|
if(!$code){ |
|
148
|
|
|
$token_fields = [ |
|
149
|
|
|
'corpid' => $this->clientId, |
|
150
|
|
|
'corpsecret' => $this->clientSecret, |
|
151
|
|
|
]; |
|
152
|
|
|
}else{ |
|
153
|
|
|
$token_fields = [ |
|
154
|
|
|
'access_token'=>$this->config['longlive_access_token'], |
|
155
|
|
|
'code'=>$code, |
|
156
|
|
|
]; |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
return $token_fields; |
|
160
|
|
|
} |
|
161
|
|
|
|
|
162
|
|
|
/** |
|
163
|
|
|
* 原始微信oauth 应该是返回 access token + openid |
|
164
|
|
|
* 企业号因为用的是7200秒的, 所以需要支持从外部去获取access_token 不会冲突 要返回 userid |
|
165
|
|
|
*/ |
|
166
|
|
|
public function getAccessToken($code) |
|
167
|
|
|
{ |
|
168
|
|
|
|
|
169
|
|
|
//没有指定则自己获取 |
|
170
|
|
|
if(!$this->config['longlive_access_token']){ |
|
171
|
|
|
$this->config['longlive_access_token'] = $this->getLongiveAccessToken(); |
|
172
|
|
|
} |
|
173
|
|
|
$param = $this->getTokenFields($code); |
|
174
|
|
|
// dd($param); |
|
|
|
|
|
|
175
|
|
|
$get_token_url = $this->user_baseinfo_api; |
|
176
|
|
|
$response = $this->getHttpClient()->get($get_token_url, [ |
|
177
|
|
|
'query' => $param, |
|
178
|
|
|
]); |
|
179
|
|
|
|
|
180
|
|
|
$content = $response->getBody()->getContents(); |
|
181
|
|
|
$content = json_decode($content,true); |
|
182
|
|
|
$content['access_token'] = $this->config['longlive_access_token']; |
|
183
|
|
|
|
|
184
|
|
|
$token = $this->parseAccessToken($content); |
|
185
|
|
|
return $token; |
|
186
|
|
|
|
|
187
|
|
|
} |
|
188
|
|
|
// !!应该尽量不要调用, 除非 单独与overture/wechat使用, 否则同时获取accesstoken, 会冲突 |
|
189
|
|
|
public function getLongiveAccessToken($forse_refresh = false){ |
|
|
|
|
|
|
190
|
|
|
$get_token_url = $this->getTokenUrl(); |
|
191
|
|
|
$response = $this->getHttpClient()->get($get_token_url, [ |
|
192
|
|
|
'query' => $this->getTokenFields(), |
|
193
|
|
|
]); |
|
194
|
|
|
$content = $response->getBody()->getContents(); |
|
195
|
|
|
$token = $this->parseAccessToken($content); |
|
196
|
|
|
return $token['access_token']; |
|
197
|
|
|
} |
|
198
|
|
|
|
|
199
|
|
|
|
|
200
|
|
|
|
|
201
|
|
|
/** |
|
202
|
|
|
* Remove the fucking callback parentheses. |
|
203
|
|
|
* |
|
204
|
|
|
* @param mixed $response |
|
205
|
|
|
* |
|
206
|
|
|
* @return string |
|
207
|
|
|
*/ |
|
208
|
|
|
protected function removeCallback($response) |
|
209
|
|
|
{ |
|
210
|
|
|
if (strpos($response, 'callback') !== false) { |
|
211
|
|
|
$lpos = strpos($response, '('); |
|
212
|
|
|
$rpos = strrpos($response, ')'); |
|
213
|
|
|
$response = substr($response, $lpos + 1, $rpos - $lpos - 1); |
|
214
|
|
|
} |
|
215
|
|
|
|
|
216
|
|
|
return $response; |
|
217
|
|
|
} |
|
218
|
|
|
} |
|
219
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.