Skip to content

Commit b0cf658

Browse files
committed
feat: 增加彩虹聚合登录功能,优化动态配置和回调处理
1 parent e87c751 commit b0cf658

7 files changed

Lines changed: 454 additions & 61 deletions

File tree

inc/ajax/page-oauth-login.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function pk_oauth_quick_buttons($echo = false, $redirect = '')
1515
$oauth_list = pk_oauth_list();
1616
$out = "<div class='d-flex justify-content-center wh100 flex-wrap'>";
1717
foreach ($oauth_list as $key => $val) {
18-
if (!isset($val['system']) || !$val['system'] || pk_is_checked('oauth_' . $key)) {
18+
if (!isset($val['system']) || !$val['system'] || pk_oauth_is_enabled($key, $val)) {
1919
$url = $val['url'] ?? pk_oauth_url_page_ajax($key, $redirect);
2020
$icon = isset($val['icon']) ? str_starts_with($val['icon'], 'http') ? "<img src='{$val['icon']}' width='15' class='mr-1' alt='{$val['label']}'/>":"<i class='{$val['icon']} mr-1'></i>" : '';
2121
$color_type = $val['color_type'] ?? 'primary';

inc/classes/PuockUserCenter.php

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public static function page_oauth()
141141

142142
$enabled_oauth_list = [];
143143
foreach ($oauth_list as $item_key => $item_val) {
144-
if (!pk_is_checked('oauth_' . $item_key)) {
144+
if (!pk_oauth_is_enabled($item_key, $item_val)) {
145145
continue;
146146
}
147147
$enabled_oauth_list[$item_key] = $item_val;
@@ -155,45 +155,62 @@ public static function page_oauth()
155155
<?php _e('站点暂未开启任何第三方登录/绑定方式', PUOCK); ?>
156156
</div>
157157
<?php else: ?>
158-
<div class="table-responsive">
159-
<table class="table table-sm align-middle">
160-
<thead>
161-
<tr>
162-
<th><?php _e('平台', PUOCK); ?></th>
163-
<th><?php _e('状态', PUOCK); ?></th>
164-
<th class="text-end"><?php _e('操作', PUOCK); ?></th>
165-
</tr>
166-
</thead>
167-
<tbody>
168-
<?php foreach ($enabled_oauth_list as $item_key => $item_val): ?>
169-
<tr>
170-
<td><?php echo esc_html($item_val['label'] ?? $item_key); ?></td>
171-
<td>
172-
<?php if (empty($item_val['openid'])): ?>
173-
<span class="badge bg-secondary"><?php _e('未绑定', PUOCK); ?></span>
174-
<?php else: ?>
175-
<span class="badge bg-success"><?php _e('已绑定', PUOCK); ?></span>
176-
<?php endif; ?>
177-
</td>
178-
<td class="text-end">
179-
<?php if (empty($item_val['openid'])): ?>
180-
<a class="btn btn-sm btn-outline-primary"
181-
target="_blank"
182-
href="<?php echo esc_url(pk_oauth_url_page_ajax($item_key, $redirect)); ?>">
183-
<?php _e('立即绑定', PUOCK); ?>
184-
</a>
185-
<?php else: ?>
186-
<a class="btn btn-sm btn-outline-danger"
187-
href="<?php echo esc_url(pk_oauth_clear_bind_url2($item_key, $redirect)); ?>"
188-
onclick="return confirm('<?php echo esc_js(__('确认解除绑定吗?', PUOCK)); ?>')">
189-
<?php _e('解除绑定', PUOCK); ?>
190-
</a>
191-
<?php endif; ?>
192-
</td>
193-
</tr>
194-
<?php endforeach; ?>
195-
</tbody>
196-
</table>
158+
<div class="d-flex flex-column gap-2 w-100">
159+
<?php foreach ($enabled_oauth_list as $item_key => $item_val):
160+
$bind_url = pk_oauth_url_page_ajax($item_key, $redirect);
161+
$unbind_url = pk_oauth_clear_bind_url2($item_key, $redirect);
162+
163+
$is_bound = !empty($item_val['openid']);
164+
$label = (string)($item_val['label'] ?? $item_key);
165+
$color_type = (string)($item_val['color_type'] ?? 'primary');
166+
$icon = isset($item_val['icon']) ? (string)$item_val['icon'] : '';
167+
?>
168+
<div class="pk-border-1 rounded px-3 py-2 d-flex align-items-center justify-content-between flex-wrap gap-3 w-100">
169+
<div class="d-flex align-items-center gap-2 flex-wrap">
170+
<?php if ($icon && (strpos($icon, 'http://') === 0 || strpos($icon, 'https://') === 0 || strpos($icon, '//') === 0)) : ?>
171+
<img
172+
src="<?php echo esc_url($icon); ?>"
173+
alt="<?php echo esc_attr($label); ?>"
174+
width="22"
175+
height="22"
176+
class="rounded-circle flex-shrink-0"
177+
/>
178+
<?php elseif ($icon) : ?>
179+
<i class="<?php echo esc_attr($icon); ?> text-<?php echo esc_attr($color_type); ?> fs-5"></i>
180+
<?php endif; ?>
181+
182+
<div class="fw-semibold lh-sm">
183+
<?php echo esc_html($label); ?>
184+
</div>
185+
186+
<?php if ($is_bound) : ?>
187+
<span class="badge bg-success"><?php _e('已绑定', PUOCK); ?></span>
188+
<?php else : ?>
189+
<span class="badge bg-secondary"><?php _e('未绑定', PUOCK); ?></span>
190+
<?php endif; ?>
191+
</div>
192+
193+
<div class="d-flex align-items-center justify-content-end">
194+
<?php if ($is_bound) : ?>
195+
<a
196+
href="<?php echo esc_url($unbind_url); ?>"
197+
class="btn btn-sm btn-outline-danger px-2 py-0"
198+
onclick="return confirm('<?php echo esc_js(__('确认解除绑定吗?', PUOCK)); ?>');"
199+
>
200+
<?php _e('解除绑定', PUOCK); ?>
201+
</a>
202+
<?php else : ?>
203+
<a
204+
href="<?php echo esc_url($bind_url); ?>"
205+
class="btn btn-sm btn-outline-<?php echo esc_attr($color_type); ?> px-2 py-0"
206+
target="_blank"
207+
>
208+
<?php _e('立即绑定', PUOCK); ?>
209+
</a>
210+
<?php endif; ?>
211+
</div>
212+
</div>
213+
<?php endforeach; ?>
197214
</div>
198215
<div class="c-sub fs12">
199216
<?php _e('绑定成功后,可使用第三方账号直接登录。', PUOCK); ?>

inc/oauth/RainbowOAuth.php

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
<?php
2+
3+
namespace Puock\Theme\oauth;
4+
5+
use InvalidArgumentException;
6+
7+
class RainbowOAuth
8+
{
9+
public $appid;
10+
11+
public $appSecret;
12+
13+
public $callbackUrl;
14+
15+
public $state;
16+
17+
public $result = [];
18+
19+
public $accessToken;
20+
21+
public $openid;
22+
23+
24+
public function __construct($appid = null, $appSecret = null, $callbackUrl = null)
25+
{
26+
$this->appid = trim((string)($appid ?? ''));
27+
$this->appSecret = trim((string)($appSecret ?? ''));
28+
$this->callbackUrl = trim((string)($callbackUrl ?? ''));
29+
}
30+
31+
public function getAuthUrl($callbackUrl = null, $state = null, $scope = null)
32+
{
33+
$this->state = $state ?: md5(uniqid('', true));
34+
35+
$redirectUri = $callbackUrl ?: $this->callbackUrl;
36+
if (empty($redirectUri))
37+
{
38+
throw new InvalidArgumentException('callbackUrl 不能为空');
39+
}
40+
41+
// Append state for CSRF validation
42+
$redirectUriWithState = add_query_arg('state', $this->state, $redirectUri);
43+
44+
$socialType = $this->getSocialType();
45+
46+
$data = $this->request([
47+
'act' => 'login',
48+
'appid' => $this->appid,
49+
'appkey' => $this->appSecret,
50+
'type' => $socialType,
51+
'redirect_uri' => $redirectUriWithState,
52+
]);
53+
54+
$url = (string)($data['url'] ?? '');
55+
if (empty($url))
56+
{
57+
throw new InvalidArgumentException('彩虹聚合登录返回的跳转地址为空');
58+
}
59+
60+
return $url;
61+
}
62+
63+
public function getAccessToken($storeState = '', $code = null, $state = null)
64+
{
65+
$state = $state ?? ($_GET['state'] ?? '');
66+
if ((string)$storeState !== (string)$state)
67+
{
68+
throw new InvalidArgumentException('state验证失败');
69+
}
70+
71+
$code = $code ?? ($_GET['code'] ?? '');
72+
if (empty($code))
73+
{
74+
throw new InvalidArgumentException('缺少回调参数 code');
75+
}
76+
77+
$socialType = $this->getSocialType();
78+
79+
$data = $this->request([
80+
'act' => 'callback',
81+
'appid' => $this->appid,
82+
'appkey' => $this->appSecret,
83+
'type' => $socialType,
84+
'code' => $code,
85+
]);
86+
87+
$this->result = $data;
88+
$this->accessToken = (string)($data['access_token'] ?? '');
89+
$this->openid = (string)($data['social_uid'] ?? '');
90+
91+
if (empty($this->openid))
92+
{
93+
throw new InvalidArgumentException('彩虹聚合登录返回 social_uid 为空');
94+
}
95+
96+
return $this->accessToken;
97+
}
98+
99+
public function getUserInfo($accessToken = null)
100+
{
101+
if (!empty($this->result))
102+
{
103+
return $this->result;
104+
}
105+
106+
if (empty($this->openid))
107+
{
108+
throw new InvalidArgumentException('openid 为空,无法查询用户信息');
109+
}
110+
111+
$socialType = $this->getSocialType();
112+
113+
$data = $this->request([
114+
'act' => 'query',
115+
'appid' => $this->appid,
116+
'appkey' => $this->appSecret,
117+
'type' => $socialType,
118+
'social_uid' => $this->openid,
119+
]);
120+
121+
$this->result = $data;
122+
return $this->result;
123+
}
124+
125+
private function getSocialType(): string
126+
{
127+
$allow = ['qq', 'wx', 'alipay', 'sina', 'baidu', 'huawei', 'xiaomi', 'douyin', 'bilibili', 'dingtalk'];
128+
129+
$providerType = sanitize_key($_GET['type'] ?? '');
130+
if (in_array($providerType, $allow, true)) {
131+
return $providerType;
132+
}
133+
134+
if (str_starts_with($providerType, 'ccy_'))
135+
{
136+
$t = substr($providerType, strlen('ccy_'));
137+
if (in_array($t, $allow, true))
138+
{
139+
return $t;
140+
}
141+
}
142+
143+
return 'qq';
144+
}
145+
146+
private function request(array $query): array
147+
{
148+
if (empty($this->appid) || empty($this->appSecret))
149+
{
150+
throw new InvalidArgumentException('彩虹聚合登录 AppID/AppKey 未配置');
151+
}
152+
153+
$apiBase = (string)(\pk_get_option('oauth_ccy_api') ?: 'https://u.cccyun.cc');
154+
$apiBase = rtrim($apiBase, '/');
155+
if (!str_starts_with($apiBase, 'http://') && !str_starts_with($apiBase, 'https://')) {
156+
throw new InvalidArgumentException('接口地址格式不正确');
157+
}
158+
159+
$apiUrl = $apiBase . '/connect.php';
160+
$url = add_query_arg($query, $apiUrl);
161+
162+
$resp = wp_remote_get($url, [
163+
'timeout' => 15,
164+
'redirection' => 0,
165+
]);
166+
167+
if (is_wp_error($resp))
168+
{
169+
throw new InvalidArgumentException('请求彩虹聚合登录失败:' . $resp->get_error_message());
170+
}
171+
172+
$body = wp_remote_retrieve_body($resp);
173+
$data = json_decode($body, true);
174+
if (!is_array($data))
175+
{
176+
throw new InvalidArgumentException('彩虹聚合登录返回数据解析失败');
177+
}
178+
179+
$code = (int)($data['code'] ?? -1);
180+
if ($code !== 0)
181+
{
182+
$msg = (string)($data['msg'] ?? '请求失败');
183+
throw new InvalidArgumentException($msg);
184+
}
185+
186+
return $data;
187+
}
188+
}

inc/oauth/callback/ccy.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
require_once dirname(__DIR__) . '/../../../../../wp-load.php';
4+
5+
$type = sanitize_key($_GET['pk_type'] ?? '');
6+
$redirect = $_GET['redirect'] ?? '';
7+
if (empty($redirect)) {
8+
$redirect = home_url('/');
9+
}
10+
11+
pk_oauth_callback_execute($type, $redirect);
12+
wp_die();

inc/oauth/callback/rainbow.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
require_once dirname(__DIR__) . '/../../../../../wp-load.php';
4+
5+
$type = sanitize_key($_GET['pk_type'] ?? '');
6+
$redirect = $_GET['redirect'] ?? '';
7+
if (empty($redirect)) {
8+
$redirect = home_url('/');
9+
}
10+
11+
pk_oauth_callback_execute($type, $redirect);
12+
wp_die();

0 commit comments

Comments
 (0)