refactor(classes): organize inc/classes into domain folders
Move ~94 root class shells and root *-traits into domain directories (core, auth, user, credits, content, payment/gateways, forum, shop, sms, ai, messages, extensions, widgets, settings, admin). Update bootstrap require paths, co-locate traits with owners, fix forum/user relative requires, drop duplicate shop controller requires, and wire home-modules load. Path-only; class names and init order unchanged.
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/**
|
||||
* AKSRT OAuth Proxy Server
|
||||
*
|
||||
* 代理登录服务端:接收客户端的代理请求,执行 OAuth 流程后将用户数据回传。
|
||||
* 服务端需要配置各平台的 OAuth 凭据。
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class AKSRT_OAuth_Proxy_Server {
|
||||
|
||||
private static $instance = null;
|
||||
|
||||
public static function get_instance(): self {
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
public function init(): void {
|
||||
add_action('template_redirect', [$this, 'handle_proxy_routes'], 5);
|
||||
add_action('init', [$this, 'add_rewrite_rules']);
|
||||
}
|
||||
|
||||
public function add_rewrite_rules(): void {
|
||||
add_rewrite_rule('^oauth/proxy/([a-z]+)/?$', 'index.php?aksrt_proxy_provider=$matches[1]', 'top');
|
||||
add_rewrite_rule('^oauth/proxy/([a-z]+)/callback/?$', 'index.php?aksrt_proxy_provider=$matches[1]&aksrt_proxy_server_callback=1', 'top');
|
||||
add_filter('query_vars', function ($vars) {
|
||||
$vars[] = 'aksrt_proxy_provider';
|
||||
$vars[] = 'aksrt_proxy_server_callback';
|
||||
return $vars;
|
||||
});
|
||||
}
|
||||
|
||||
public function handle_proxy_routes(): void {
|
||||
$provider = sanitize_key(get_query_var('aksrt_proxy_provider'));
|
||||
if (empty($provider)) return;
|
||||
|
||||
if (AKSRT_Settings::oauth_proxy_mode() !== 'server') return;
|
||||
|
||||
$allowed = AKSRT_Settings::oauth_proxy_allowed_providers();
|
||||
if (!in_array($provider, $allowed, true)) {
|
||||
wp_die(__('不支持的代理登录平台', 'aksrt'));
|
||||
}
|
||||
|
||||
$is_callback = !empty(get_query_var('aksrt_proxy_server_callback'));
|
||||
if ($is_callback) {
|
||||
$this->handle_proxy_callback($provider);
|
||||
} else {
|
||||
$this->handle_proxy_request($provider);
|
||||
}
|
||||
}
|
||||
|
||||
private function handle_proxy_request(string $provider): void {
|
||||
$secret = AKSRT_Settings::oauth_proxy_secret();
|
||||
if (empty($secret)) {
|
||||
wp_die(__('代理登录服务端未配置密钥', 'aksrt'));
|
||||
}
|
||||
|
||||
$agent_back_url = esc_url_raw($_GET['agent_back_url'] ?? '');
|
||||
$sign = sanitize_text_field($_GET['sign'] ?? '');
|
||||
$action = sanitize_key($_GET['action'] ?? 'login');
|
||||
|
||||
if (empty($agent_back_url) || empty($sign)) {
|
||||
wp_die(__('代理请求参数不完整', 'aksrt'));
|
||||
}
|
||||
|
||||
$verify_params = $_GET;
|
||||
unset($verify_params['sign']);
|
||||
if (!$this->verify_sign($verify_params, $secret, $sign)) {
|
||||
wp_die(__('代理请求签名验证失败', 'aksrt'));
|
||||
}
|
||||
|
||||
$ts = (int) ($_GET['ts'] ?? 0);
|
||||
if (abs(time() - $ts) > 300) {
|
||||
wp_die(__('代理请求已过期,请重试', 'aksrt'));
|
||||
}
|
||||
|
||||
@session_start();
|
||||
$_SESSION['aksrt_proxy_agent_back_url'] = $agent_back_url;
|
||||
$_SESSION['aksrt_proxy_action'] = $action;
|
||||
$_SESSION['aksrt_proxy_nonce'] = sanitize_text_field($_GET['nonce'] ?? '');
|
||||
$_SESSION['aksrt_proxy_uid'] = (int) ($_GET['uid'] ?? 0);
|
||||
|
||||
try {
|
||||
$oauth = AKSRT_OAuth::get_instance();
|
||||
$auth_url = $oauth->get_auth_url($provider, $action);
|
||||
if (empty($auth_url)) {
|
||||
wp_die(__('无法生成授权链接', 'aksrt'));
|
||||
}
|
||||
wp_safe_redirect($auth_url);
|
||||
exit;
|
||||
} catch (\Exception $e) {
|
||||
wp_die(__('代理登录错误:', 'aksrt') . esc_html($e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private function handle_proxy_callback(string $provider): void {
|
||||
@session_start();
|
||||
|
||||
$agent_back_url = $_SESSION['aksrt_proxy_agent_back_url'] ?? '';
|
||||
if (empty($agent_back_url)) return;
|
||||
|
||||
$_SESSION['aksrt_proxy_agent_back_url'] = '';
|
||||
$_SESSION['aksrt_proxy_action'] = '';
|
||||
$_SESSION['aksrt_proxy_nonce'] = '';
|
||||
$_SESSION['aksrt_proxy_uid'] = 0;
|
||||
|
||||
$secret = AKSRT_Settings::oauth_proxy_secret();
|
||||
if (empty($secret)) {
|
||||
wp_die(__('代理登录服务端配置错误', 'aksrt'));
|
||||
}
|
||||
|
||||
$error = $_GET['error'] ?? '';
|
||||
if ($error) {
|
||||
wp_safe_redirect(add_query_arg(['error' => urlencode($error)], $agent_back_url));
|
||||
exit;
|
||||
}
|
||||
|
||||
$code = sanitize_text_field($_GET['code'] ?? '');
|
||||
if (empty($code)) {
|
||||
wp_safe_redirect(add_query_arg(['error' => urlencode('missing_code')], $agent_back_url));
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$user_data = $this->fetch_user_data($provider, $code);
|
||||
|
||||
if (empty($user_data['provider_id'])) {
|
||||
wp_safe_redirect(add_query_arg(['error' => urlencode('no_user_info')], $agent_back_url));
|
||||
exit;
|
||||
}
|
||||
|
||||
$callback_params = [
|
||||
'provider' => $provider,
|
||||
'provider_id' => $user_data['provider_id'],
|
||||
'username' => $user_data['username'] ?? '',
|
||||
'name' => $user_data['name'] ?? '',
|
||||
'email' => $user_data['email'] ?? '',
|
||||
'email_verified' => !empty($user_data['email_verified']) ? '1' : '0',
|
||||
'avatar_url' => $user_data['avatar_url'] ?? '',
|
||||
'bio' => $user_data['bio'] ?? '',
|
||||
'action' => sanitize_key($_SESSION['aksrt_proxy_action'] ?? 'login'),
|
||||
'ts' => time(),
|
||||
];
|
||||
|
||||
$callback_params['sign'] = $this->sign($callback_params, $secret);
|
||||
$redirect_url = $agent_back_url . (strpos($agent_back_url, '?') !== false ? '&' : '?') . http_build_query($callback_params);
|
||||
wp_safe_redirect($redirect_url);
|
||||
exit;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
wp_safe_redirect(add_query_arg(['error' => urlencode($e->getMessage())], $agent_back_url));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户数据
|
||||
*/
|
||||
private function fetch_user_data(string $provider, string $code): array {
|
||||
$config = get_option('aksrt_oauth', [])[$provider] ?? [];
|
||||
if (empty($config['client_id']) || empty($config['client_secret'])) {
|
||||
throw new \Exception(__('未配置此平台的 OAuth 凭据', 'aksrt'));
|
||||
}
|
||||
|
||||
$redirect_uri = !empty($config['redirect_uri'])
|
||||
? $config['redirect_uri']
|
||||
: home_url('/oauth/proxy/' . $provider . '/callback');
|
||||
|
||||
switch ($provider) {
|
||||
case 'github':
|
||||
$oauth = new \Yurun\OAuthLogin\Github\OAuth2(
|
||||
$config['client_id'],
|
||||
$config['client_secret'],
|
||||
$redirect_uri
|
||||
);
|
||||
$oauth->getAccessToken($_GET['state'] ?? '', $code, $_GET['state'] ?? null);
|
||||
$userInfo = $oauth->getUserInfo();
|
||||
|
||||
$email = $userInfo['email'] ?? '';
|
||||
if (empty($email)) {
|
||||
$emails_json = $oauth->http->header('Authorization', ' token ' . $oauth->accessToken)
|
||||
->get('https://api.github.com/user/emails')
|
||||
->body();
|
||||
$emails = json_decode($emails_json, true) ?: [];
|
||||
foreach ($emails as $em) {
|
||||
if (!empty($em['primary']) && !empty($em['email'])) {
|
||||
$email = $em['email'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'provider' => 'github',
|
||||
'provider_id' => (string) ($userInfo['id'] ?? ''),
|
||||
'username' => $userInfo['login'] ?? '',
|
||||
'name' => $userInfo['name'] ?? '',
|
||||
'email' => $email,
|
||||
'avatar_url' => $userInfo['avatar_url'] ?? '',
|
||||
'bio' => $userInfo['bio'] ?? '',
|
||||
];
|
||||
|
||||
case 'gitee':
|
||||
$oauth = new \Yurun\OAuthLogin\Gitee\OAuth2(
|
||||
$config['client_id'],
|
||||
$config['client_secret'],
|
||||
$redirect_uri
|
||||
);
|
||||
$oauth->getAccessToken($_GET['state'] ?? '', $code, $_GET['state'] ?? null);
|
||||
$userInfo = $oauth->getUserInfo();
|
||||
|
||||
return [
|
||||
'provider' => 'gitee',
|
||||
'provider_id' => (string) ($userInfo['id'] ?? ''),
|
||||
'username' => $userInfo['login'] ?? '',
|
||||
'name' => $userInfo['name'] ?? '',
|
||||
'email' => $userInfo['email'] ?? '',
|
||||
'avatar_url' => $userInfo['avatar_url'] ?? '',
|
||||
'bio' => $userInfo['bio'] ?? '',
|
||||
];
|
||||
|
||||
case 'qq':
|
||||
$oauth = new \Yurun\OAuthLogin\QQ\OAuth2(
|
||||
$config['client_id'],
|
||||
$config['client_secret'],
|
||||
$redirect_uri
|
||||
);
|
||||
$oauth->getAccessToken($_GET['state'] ?? '', $code, $_GET['state'] ?? null);
|
||||
$openid = $oauth->getOpenID();
|
||||
$userInfo = $oauth->getUserInfo();
|
||||
|
||||
return [
|
||||
'provider' => 'qq',
|
||||
'provider_id' => $openid,
|
||||
'username' => $userInfo['nickname'] ?? '',
|
||||
'name' => $userInfo['nickname'] ?? '',
|
||||
'email' => '',
|
||||
'avatar_url' => $userInfo['figureurl_qq_2'] ?? $userInfo['figureurl_2'] ?? '',
|
||||
];
|
||||
|
||||
case 'google':
|
||||
$response = wp_remote_post('https://oauth2.googleapis.com/token', [
|
||||
'body' => [
|
||||
'code' => $code,
|
||||
'client_id' => $config['client_id'],
|
||||
'client_secret' => $config['client_secret'],
|
||||
'redirect_uri' => $redirect_uri,
|
||||
'grant_type' => 'authorization_code',
|
||||
],
|
||||
]);
|
||||
if (is_wp_error($response)) throw new \Exception($response->get_error_message());
|
||||
$token_data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
if (empty($token_data['access_token'])) throw new \Exception(__('Google token 获取失败', 'aksrt'));
|
||||
|
||||
$user_response = wp_remote_get('https://www.googleapis.com/oauth2/v2/userinfo', [
|
||||
'headers' => ['Authorization' => 'Bearer ' . $token_data['access_token']],
|
||||
]);
|
||||
if (is_wp_error($user_response)) throw new \Exception($user_response->get_error_message());
|
||||
$user_info = json_decode(wp_remote_retrieve_body($user_response), true);
|
||||
|
||||
return [
|
||||
'provider' => 'google',
|
||||
'provider_id' => (string) ($user_info['id'] ?? ''),
|
||||
'username' => $user_info['email'] ?? '',
|
||||
'name' => $user_info['name'] ?? '',
|
||||
'email' => $user_info['email'] ?? '',
|
||||
'avatar_url' => $user_info['picture'] ?? '',
|
||||
];
|
||||
|
||||
default:
|
||||
throw new \Exception(__('不支持的 OAuth 平台', 'aksrt'));
|
||||
}
|
||||
}
|
||||
|
||||
private function sign(array $data, string $secret): string {
|
||||
unset($data['sign']);
|
||||
ksort($data);
|
||||
return hash_hmac('sha256', http_build_query($data), $secret);
|
||||
}
|
||||
|
||||
private function verify_sign(array $data, string $secret, string $expected_sign): bool {
|
||||
return hash_equals($this->sign($data, $secret), $expected_sign);
|
||||
}
|
||||
}
|
||||
|
||||
return AKSRT_OAuth_Proxy_Server::get_instance();
|
||||
Reference in New Issue
Block a user