Move all domain folders from src/AKSRT/{Domain} to src/{Domain}; remap
Composer PSR-4 "AKSRT\\": "src/"; update audit script + README + ADR
paths; remove dead tools/build-psr4-map.php (scanned deleted inc/classes).
616 lines
25 KiB
PHP
616 lines
25 KiB
PHP
<?php
|
||
/**
|
||
* @package AKSRT
|
||
*/
|
||
|
||
namespace AKSRT\Auth;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
trait OAuthPart1 {
|
||
|
||
private function get_baidu_user_data(array $config, string $code, string $redirect_uri): array {
|
||
$oauth = new \Yurun\OAuthLogin\Baidu\OAuth2(
|
||
$config['client_id'],
|
||
$config['client_secret'],
|
||
$redirect_uri
|
||
);
|
||
|
||
$oauth->getAccessToken($_GET['state'] ?? '', $code, $_GET['state'] ?? null);
|
||
$userInfo = $oauth->getUserInfo();
|
||
|
||
return [
|
||
'provider' => 'baidu',
|
||
'provider_id' => (string) ($userInfo['userid'] ?? ''),
|
||
'username' => $userInfo['username'] ?? '',
|
||
'name' => $userInfo['realname'] ?? $userInfo['username'] ?? '',
|
||
'email' => $userInfo['email'] ?? '',
|
||
'avatar_url' => $userInfo['portrait'] ? ('https://himg.bdimg.com/sys/portrait/item/' . $userInfo['portrait'] . '.jpg') : '',
|
||
];
|
||
}
|
||
|
||
private function get_alipay_user_data(array $config, string $code, string $redirect_uri): array {
|
||
$oauth = new \Yurun\OAuthLogin\Alipay\OAuth2(
|
||
$config['client_id'],
|
||
$config['client_secret'],
|
||
$redirect_uri
|
||
);
|
||
|
||
$oauth->getAccessToken($_GET['state'] ?? '', $code, $_GET['state'] ?? null);
|
||
$userInfo = $oauth->getUserInfo();
|
||
|
||
return [
|
||
'provider' => 'alipay',
|
||
'provider_id' => (string) ($userInfo['user_id'] ?? ''),
|
||
'username' => $userInfo['user_name'] ?? '',
|
||
'name' => $userInfo['user_name'] ?? '',
|
||
'email' => $userInfo['email'] ?? '',
|
||
'avatar_url' => $userInfo['avatar'] ?? '',
|
||
'gender' => ($userInfo['gender'] ?? '') === 'm' ? 'male' : (($userInfo['gender'] ?? '') === 'f' ? 'female' : ''),
|
||
];
|
||
}
|
||
|
||
private function get_google_auth_url(array $config, string $redirect_uri, string $state): string {
|
||
$params = http_build_query([
|
||
'client_id' => $config['client_id'],
|
||
'redirect_uri' => $redirect_uri,
|
||
'response_type' => 'code',
|
||
'scope' => 'openid email profile',
|
||
'state' => $state,
|
||
'access_type' => 'offline',
|
||
]);
|
||
return 'https://accounts.google.com/o/oauth2/v2/auth?' . $params;
|
||
}
|
||
|
||
private function get_google_user_data(array $config, string $code, string $redirect_uri): array {
|
||
// 换取 token
|
||
$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'] ?? '',
|
||
'email_verified' => !empty($user_info['verified_email']),
|
||
'avatar_url' => $user_info['picture'] ?? '',
|
||
'locale' => $user_info['locale'] ?? '',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 微信公众号 OAuth2 授权 URL
|
||
*
|
||
* 使用 snsapi_userinfo scope 获取用户信息(需已认证服务号或订阅号+验证)。
|
||
*/
|
||
private function get_weixingzh_auth_url(array $config, string $redirect_uri, string $state): string {
|
||
$params = http_build_query([
|
||
'appid' => $config['client_id'],
|
||
'redirect_uri' => $redirect_uri,
|
||
'response_type' => 'code',
|
||
'scope' => 'snsapi_userinfo',
|
||
'state' => $state,
|
||
]);
|
||
return 'https://open.weixin.qq.com/connect/oauth2/authorize?' . $params . '#wechat_redirect';
|
||
}
|
||
|
||
/**
|
||
* 微信公众号 OAuth2 获取用户信息
|
||
*
|
||
* 1. code → access_token + openid
|
||
* 2. access_token + openid → userinfo
|
||
*/
|
||
private function get_weixingzh_user_data(array $config, string $code, string $redirect_uri): array {
|
||
// 1. 换取 access_token
|
||
$token_url = add_query_arg([
|
||
'appid' => $config['client_id'],
|
||
'secret' => $config['client_secret'],
|
||
'code' => $code,
|
||
'grant_type' => 'authorization_code',
|
||
], 'https://api.weixin.qq.com/sns/oauth2/access_token');
|
||
|
||
$response = wp_remote_get($token_url);
|
||
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']) || empty($token_data['openid'])) {
|
||
$errmsg = $token_data['errmsg'] ?? __('微信公众号 token 获取失败', 'aksrt');
|
||
throw new \Exception($errmsg);
|
||
}
|
||
|
||
// 2. 获取用户信息
|
||
$user_url = add_query_arg([
|
||
'access_token' => $token_data['access_token'],
|
||
'openid' => $token_data['openid'],
|
||
'lang' => 'zh_CN',
|
||
], 'https://api.weixin.qq.com/sns/userinfo');
|
||
|
||
$user_response = wp_remote_get($user_url);
|
||
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' => 'weixingzh',
|
||
'provider_id' => (string) ($user_info['openid'] ?? $token_data['openid']),
|
||
'username' => $user_info['nickname'] ?? '',
|
||
'name' => $user_info['nickname'] ?? '',
|
||
'email' => '',
|
||
'email_verified' => false,
|
||
'avatar_url' => $user_info['headimgurl'] ?? '',
|
||
'locale' => 'zh_CN',
|
||
'unionid' => $user_info['unionid'] ?? '',
|
||
];
|
||
}
|
||
|
||
private function resolve_callback_provider(): string {
|
||
$state = $_GET['state'] ?? '';
|
||
if ($state === '') return '';
|
||
|
||
$rest = (strpos($state, 'bind_') === 0) ? substr($state, 5) : $state;
|
||
$sep = strpos($rest, '_');
|
||
if ($sep !== false && $sep < strlen($rest) - 1) {
|
||
return sanitize_key(substr($rest, 0, $sep));
|
||
}
|
||
return '';
|
||
}
|
||
|
||
private function get_enabled_providers(): array {
|
||
if (class_exists('\AKSRT\Settings\Settings') && \AKSRT\Settings\Settings::oauth_proxy_mode() === 'client') {
|
||
return \AKSRT\Settings\Settings::oauth_proxy_client_providers();
|
||
}
|
||
|
||
$providers = get_option('aksrt_oauth', []);
|
||
$enabled = [];
|
||
foreach (['github', 'gitee', 'qq', 'google', 'weibo', 'weixin', 'baidu', 'alipay', 'weixingzh'] as $key) {
|
||
if (!empty($providers[$key]['enabled']) && !empty($providers[$key]['client_id'])) {
|
||
$enabled[] = $key;
|
||
continue;
|
||
}
|
||
// BC: also check aksrt_settings flat keys
|
||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||
$s = \AKSRT\Settings\Settings::get_all();
|
||
$switch_key = 'oauth_' . $key . '_s';
|
||
if (!empty($s[$switch_key])) {
|
||
$id_key = $key . '_appid';
|
||
$sec_key = $key . '_appsecret';
|
||
if ($key === 'weixingzh') {
|
||
$sec_key = 'weixingzh_appsecret';
|
||
} elseif ($key === 'github' || $key === 'gitee' || $key === 'qq') {
|
||
$sec_key = $key . '_appkey';
|
||
}
|
||
if (!empty($s[$id_key]) && !empty($s[$sec_key])) {
|
||
$enabled[] = $key;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// Rainbow aggregate (clogin) providers as virtual keys: clogin_qq, clogin_weixin...
|
||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||
$s = \AKSRT\Settings\Settings::get_all();
|
||
if (!empty($s['clogin_s']) && !empty($s['clogin_url']) && !empty($s['clogin_id']) && !empty($s['clogin_key'])) {
|
||
$raw = $s['clogin_oauth_s'] ?? $s['clogin_types'] ?? ['qq', 'weixin', 'weibo', 'alipay'];
|
||
if (is_string($raw)) {
|
||
$raw = preg_split('/[,,\s]+/', $raw) ?: [];
|
||
}
|
||
if (!is_array($raw)) {
|
||
$raw = [];
|
||
}
|
||
foreach ($raw as $ctype) {
|
||
$ctype = sanitize_key((string) $ctype);
|
||
if ($ctype === '') {
|
||
continue;
|
||
}
|
||
$enabled[] = 'clogin_' . $ctype;
|
||
}
|
||
}
|
||
}
|
||
|
||
return array_values(array_unique($enabled));
|
||
}
|
||
|
||
/**
|
||
* 获取 OAuth 提供商数据(供模板使用)
|
||
* 返回 [{name, label, icon, url}, ...]
|
||
*/
|
||
public function get_auth_providers_data(): array {
|
||
$platform_names = [
|
||
'github' => 'GitHub',
|
||
'gitee' => 'Gitee',
|
||
'qq' => 'QQ',
|
||
'google' => 'Google',
|
||
'weibo' => 'Weibo',
|
||
'weixin' => 'WeChat',
|
||
'baidu' => 'Baidu',
|
||
'alipay' => 'Alipay',
|
||
'weixingzh' => '微信公众号',
|
||
];
|
||
|
||
$result = [];
|
||
foreach ($this->get_enabled_providers() as $key) {
|
||
$url = $this->get_auth_url($key);
|
||
if (empty($url)) continue;
|
||
|
||
$label = $platform_names[$key] ?? '';
|
||
$icon_key = $key;
|
||
if (str_starts_with($key, 'clogin_')) {
|
||
$ctype = substr($key, 7);
|
||
$label = ($platform_names[$ctype] ?? strtoupper($ctype)) . ' · 聚合';
|
||
$icon_key = $ctype === 'weixin' ? 'weixin' : $ctype;
|
||
}
|
||
if ($label === '') {
|
||
$label = ucfirst($key);
|
||
}
|
||
$result[] = [
|
||
'name' => $key,
|
||
'label' => $label,
|
||
'icon' => function_exists('aksrt_brand_icon') ? aksrt_brand_icon((string) $icon_key) : '',
|
||
'url' => esc_url($url),
|
||
];
|
||
}
|
||
return $result;
|
||
}
|
||
|
||
private function get_provider_config(string $provider): ?array {
|
||
$providers = get_option('aksrt_oauth', []);
|
||
$config = $providers[$provider] ?? null;
|
||
if (!empty($config['client_id']) && !empty($config['client_secret'])) {
|
||
return $config;
|
||
}
|
||
|
||
// BC: fallback to aksrt_settings flat keys
|
||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||
$s = \AKSRT\Settings\Settings::get_all();
|
||
$id_key = $provider . '_appid';
|
||
$sec_key = $provider . '_appsecret';
|
||
if ($provider === 'github' || $provider === 'gitee' || $provider === 'qq') {
|
||
$sec_key = $provider . '_appkey';
|
||
} elseif ($provider === 'google') {
|
||
$id_key = 'google_client_id';
|
||
$sec_key = 'google_client_secret';
|
||
}
|
||
$client_id = (string) ($s[$id_key] ?? '');
|
||
$client_secret = (string) ($s[$sec_key] ?? '');
|
||
if ($client_id !== '' && $client_secret !== '') {
|
||
return [
|
||
'client_id' => $client_id,
|
||
'client_secret' => $client_secret,
|
||
'redirect_uri' => home_url('/oauth/callback/'),
|
||
];
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private function find_or_create_user($provider, $user_data) {
|
||
$email = $user_data['email'] ?? '';
|
||
|
||
if (!empty($user_data['provider_id'])) {
|
||
$by_provider = $this->find_user_by_provider($provider, (string) $user_data['provider_id']);
|
||
if ($by_provider instanceof \WP_User) {
|
||
$bindings = get_user_meta($by_provider->ID, 'aksrt_oauth_bindings', true) ?: [];
|
||
if (!is_array($bindings)) $bindings = [];
|
||
if (!in_array($provider, $bindings, true)) {
|
||
$bindings[] = $provider;
|
||
update_user_meta($by_provider->ID, 'aksrt_oauth_bindings', $bindings);
|
||
}
|
||
if (!empty($user_data['avatar_url'])) {
|
||
update_user_meta($by_provider->ID, 'aksrt_oauth_avatar', esc_url_raw($user_data['avatar_url']));
|
||
}
|
||
return $by_provider;
|
||
}
|
||
}
|
||
|
||
if (!empty($email)) {
|
||
$existing = get_user_by('email', $email);
|
||
if ($existing && !$existing instanceof \WP_Error) {
|
||
$bindings = get_user_meta($existing->ID, 'aksrt_oauth_bindings', true) ?: [];
|
||
if (!is_array($bindings)) $bindings = [];
|
||
if (!in_array($provider, $bindings, true)) {
|
||
$bindings[] = $provider;
|
||
update_user_meta($existing->ID, 'aksrt_oauth_bindings', $bindings);
|
||
}
|
||
if (!empty($user_data['provider_id'])) {
|
||
update_user_meta($existing->ID, "aksrt_{$provider}_id", (string) $user_data['provider_id']);
|
||
}
|
||
if (!empty($user_data['avatar_url'])) {
|
||
update_user_meta($existing->ID, 'aksrt_oauth_avatar', esc_url_raw($user_data['avatar_url']));
|
||
}
|
||
return $existing;
|
||
}
|
||
}
|
||
|
||
return new \WP_Error(
|
||
'oauth_no_match',
|
||
__('需要绑定已有账号', 'aksrt'),
|
||
[
|
||
'provider' => $provider,
|
||
'user_data' => $user_data,
|
||
]
|
||
);
|
||
}
|
||
|
||
// ============================================
|
||
// 前端UI方法
|
||
// ============================================
|
||
|
||
public function login_button(): void {
|
||
echo $this->render_social_buttons(__('使用以下方式快速登录', 'aksrt'));
|
||
}
|
||
|
||
public function register_button(): void {
|
||
echo $this->render_social_buttons(__('使用以下方式快速注册', 'aksrt'));
|
||
}
|
||
|
||
public function render_social_buttons(string $title = ''): string {
|
||
$items = $this->get_auth_providers_data();
|
||
if (empty($items)) {
|
||
return '';
|
||
}
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="social-login-divider">
|
||
<span><?php echo esc_html($title) ?: esc_html__('or', 'aksrt'); ?></span>
|
||
</div>
|
||
<div class="oauth-row">
|
||
<?php foreach ($items as $item):
|
||
$key = (string) ($item['name'] ?? '');
|
||
$url = (string) ($item['url'] ?? '');
|
||
if ($key === '' || $url === '') continue;
|
||
?>
|
||
<a href="<?php echo esc_url($url); ?>"
|
||
class="oauth-btn oauth-btn--<?php echo esc_attr($key); ?>"
|
||
data-provider="<?php echo esc_attr($key); ?>"
|
||
aria-label="<?php echo esc_attr((string) ($item['label'] ?? $key)); ?>"
|
||
title="<?php echo esc_attr((string) ($item['label'] ?? $key)); ?>">
|
||
<?php echo $item['icon'] ?? ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||
</a>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
public function get_login_buttons(): string {
|
||
return $this->render_social_buttons();
|
||
}
|
||
|
||
public function login_message(string $message): string {
|
||
return $message . self::get_instance()->render_social_buttons();
|
||
}
|
||
|
||
public function register_message(string $message): string {
|
||
return $message . self::get_instance()->render_social_buttons(__('或使用社交账号快速注册', 'aksrt'));
|
||
}
|
||
|
||
/**
|
||
* @return array{enabled:bool,url:string,id:string,key:string,types:string[]}
|
||
*/
|
||
private function get_clogin_settings(): array {
|
||
$s = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
|
||
$raw = $s['clogin_oauth_s'] ?? $s['clogin_types'] ?? ['qq', 'weixin', 'weibo', 'alipay'];
|
||
if (is_string($raw)) {
|
||
$raw = preg_split('/[,,\s]+/', $raw) ?: [];
|
||
}
|
||
if (!is_array($raw)) {
|
||
$raw = [];
|
||
}
|
||
$types = [];
|
||
foreach ($raw as $t) {
|
||
$t = sanitize_key((string) $t);
|
||
if ($t !== '') {
|
||
$types[] = $t;
|
||
}
|
||
}
|
||
return [
|
||
'enabled' => !empty($s['clogin_s']),
|
||
'url' => esc_url_raw((string) ($s['clogin_url'] ?? '')),
|
||
'id' => (string) ($s['clogin_id'] ?? ''),
|
||
'key' => (string) ($s['clogin_key'] ?? ''),
|
||
'types' => array_values(array_unique($types)),
|
||
];
|
||
}
|
||
|
||
private function get_clogin_login_url(string $type, string $action = 'login'): string {
|
||
$cfg = $this->get_clogin_settings();
|
||
$type = sanitize_key($type);
|
||
if (!$cfg['enabled'] || $cfg['url'] === '' || $cfg['id'] === '' || $cfg['key'] === '' || $type === '') {
|
||
return '';
|
||
}
|
||
if (!empty($cfg['types']) && !in_array($type, $cfg['types'], true)) {
|
||
return '';
|
||
}
|
||
$args = ['type' => $type];
|
||
if ($action === 'bind' && is_user_logged_in()) {
|
||
$args['bind'] = 1;
|
||
}
|
||
$rurl = isset($_GET['redirect_to']) ? esc_url_raw(wp_unslash((string) $_GET['redirect_to'])) : '';
|
||
if ($rurl !== '') {
|
||
$args['rurl'] = $rurl;
|
||
}
|
||
return add_query_arg($args, home_url('/oauth/clogin/'));
|
||
}
|
||
|
||
public function handle_clogin_routes(): void {
|
||
$route = get_query_var('aksrt_oauth');
|
||
if ($route === 'clogin') {
|
||
$this->handle_clogin_login();
|
||
return;
|
||
}
|
||
if ($route === 'clogin_callback') {
|
||
$this->handle_clogin_callback();
|
||
}
|
||
}
|
||
|
||
private function handle_clogin_login(): void {
|
||
$cfg = $this->get_clogin_settings();
|
||
$type = isset($_GET['type']) ? sanitize_key(wp_unslash((string) $_GET['type'])) : '';
|
||
if (!$cfg['enabled'] || $cfg['url'] === '' || $cfg['id'] === '' || $cfg['key'] === '' || $type === '') {
|
||
wp_die(esc_html__('暂未启用此登录方式', 'aksrt'));
|
||
}
|
||
if (!empty($cfg['types']) && !in_array($type, $cfg['types'], true)) {
|
||
wp_die(esc_html__('此登录方式未启用', 'aksrt'));
|
||
}
|
||
if (!class_exists(CLoginClient::class)) {
|
||
wp_die(esc_html__('聚合登录组件未加载', 'aksrt'));
|
||
}
|
||
|
||
$client = new CLoginClient([
|
||
'apiurl' => $cfg['url'],
|
||
'appid' => $cfg['id'],
|
||
'appkey' => $cfg['key'],
|
||
'callback' => home_url('/oauth/clogin/callback'),
|
||
]);
|
||
$resp = $client->login($type);
|
||
if (!empty($resp['code']) && (int) $resp['code'] !== 0 && !empty($resp['msg'])) {
|
||
wp_die(esc_html(sprintf(__('处理失败:%s', 'aksrt'), (string) $resp['msg'])));
|
||
}
|
||
if (empty($resp['url'])) {
|
||
wp_die(esc_html__('无法获取聚合登录地址', 'aksrt'));
|
||
}
|
||
|
||
if (!session_id()) {
|
||
@session_start();
|
||
}
|
||
$_SESSION['AKSRT_CLOGIN_STATE'] = $client->state;
|
||
$_SESSION['AKSRT_CLOGIN_TYPE'] = $type;
|
||
$_SESSION['AKSRT_CLOGIN_RURL'] = !empty($_GET['rurl']) ? esc_url_raw(wp_unslash((string) $_GET['rurl'])) : '';
|
||
$_SESSION['AKSRT_CLOGIN_BIND'] = !empty($_GET['bind']) ? 1 : 0;
|
||
|
||
wp_redirect(esc_url_raw((string) $resp['url']));
|
||
exit;
|
||
}
|
||
|
||
private function handle_clogin_callback(): void {
|
||
if (!session_id()) {
|
||
@session_start();
|
||
}
|
||
$state = isset($_GET['state']) ? (string) wp_unslash($_GET['state']) : '';
|
||
$code = isset($_GET['code']) ? (string) wp_unslash($_GET['code']) : '';
|
||
if ($code === '' || $state === '' || empty($_SESSION['AKSRT_CLOGIN_STATE']) || !hash_equals((string) $_SESSION['AKSRT_CLOGIN_STATE'], $state)) {
|
||
wp_die(esc_html__('环境异常,state 验证失败,请刷新后重试', 'aksrt'));
|
||
}
|
||
|
||
$cfg = $this->get_clogin_settings();
|
||
if (!$cfg['enabled']) {
|
||
wp_die(esc_html__('暂未启用此功能', 'aksrt'));
|
||
}
|
||
$client = new CLoginClient([
|
||
'apiurl' => $cfg['url'],
|
||
'appid' => $cfg['id'],
|
||
'appkey' => $cfg['key'],
|
||
]);
|
||
$info = $client->callback($code);
|
||
if (!empty($info['code']) && (int) $info['code'] !== 0 && !empty($info['msg'])) {
|
||
wp_die(esc_html((string) $info['msg']));
|
||
}
|
||
$openid = (string) ($info['social_uid'] ?? '');
|
||
$api_type = (string) ($info['type'] ?? ($_SESSION['AKSRT_CLOGIN_TYPE'] ?? ''));
|
||
$provider_type = $client->from_api_type($api_type);
|
||
if ($openid === '' || $provider_type === '') {
|
||
wp_die(esc_html__('无法获取用户信息', 'aksrt'));
|
||
}
|
||
|
||
$provider = 'clogin_' . sanitize_key($provider_type);
|
||
$user_data = [
|
||
'provider' => $provider,
|
||
'provider_id' => $openid,
|
||
'username' => (string) ($info['nickname'] ?? ''),
|
||
'name' => (string) ($info['nickname'] ?? ''),
|
||
'email' => '',
|
||
'avatar_url' => (string) ($info['faceimg'] ?? ''),
|
||
];
|
||
if ($user_data['avatar_url'] !== '' && str_contains($user_data['avatar_url'], 'no_portrait')) {
|
||
$user_data['avatar_url'] = '';
|
||
}
|
||
|
||
$is_bind = !empty($_SESSION['AKSRT_CLOGIN_BIND']);
|
||
$rurl = !empty($_SESSION['AKSRT_CLOGIN_RURL']) ? (string) $_SESSION['AKSRT_CLOGIN_RURL'] : '';
|
||
unset($_SESSION['AKSRT_CLOGIN_STATE'], $_SESSION['AKSRT_CLOGIN_TYPE'], $_SESSION['AKSRT_CLOGIN_RURL'], $_SESSION['AKSRT_CLOGIN_BIND']);
|
||
|
||
try {
|
||
if ($is_bind) {
|
||
$uid = get_current_user_id();
|
||
if ($uid <= 0) {
|
||
wp_safe_redirect(function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url());
|
||
exit;
|
||
}
|
||
$existing = $this->find_user_by_provider($provider, $openid);
|
||
if ($existing && (int) $existing->ID !== $uid) {
|
||
throw new \Exception(__('该第三方账号已被其他用户绑定', 'aksrt'));
|
||
}
|
||
$this->bind_provider_to_user($uid, $provider, $user_data);
|
||
$redirect = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : home_url('/');
|
||
wp_safe_redirect(add_query_arg(['tab' => 'settings', 'oauth_action' => 'bind', 'oauth_bound' => $provider], $redirect));
|
||
exit;
|
||
}
|
||
|
||
$user = $this->find_or_create_user($provider, $user_data);
|
||
if (is_wp_error($user) && $user->get_error_code() === 'oauth_no_match') {
|
||
// Auto-create account for aggregate login when no match (zibll-like).
|
||
$login = 'u' . wp_generate_password(10, false, false);
|
||
$uid = wp_insert_user([
|
||
'user_login' => $login,
|
||
'user_pass' => wp_generate_password(24, true, true),
|
||
'display_name' => $user_data['name'] !== '' ? $user_data['name'] : $login,
|
||
'nickname' => $user_data['name'] !== '' ? $user_data['name'] : $login,
|
||
'role' => get_option('default_role', 'subscriber'),
|
||
]);
|
||
if (is_wp_error($uid)) {
|
||
throw new \Exception($uid->get_error_message());
|
||
}
|
||
$this->bind_provider_to_user((int) $uid, $provider, $user_data);
|
||
$user = get_user_by('id', (int) $uid);
|
||
}
|
||
if (is_wp_error($user) || !$user instanceof \WP_User) {
|
||
throw new \Exception(is_wp_error($user) ? $user->get_error_message() : __('登录失败', 'aksrt'));
|
||
}
|
||
wp_set_current_user($user->ID);
|
||
wp_set_auth_cookie($user->ID, true);
|
||
do_action('wp_login', $user->user_login, $user);
|
||
|
||
$redirect = $rurl !== '' ? $rurl : (function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : home_url('/'));
|
||
wp_safe_redirect(add_query_arg(['oauth_action' => 'login', 'oauth_bound' => $provider], $redirect));
|
||
exit;
|
||
} catch (\Exception $e) {
|
||
wp_die(esc_html__('登录失败:', 'aksrt') . '<br>' . esc_html($e->getMessage()));
|
||
}
|
||
}
|
||
|
||
|
||
}
|