WordPress QQ login function can't get profile avatars and nicknames, and doesn't work on mobile devices

这是我拿一个插件对着官方文档修复的,之前无法使用;
里面原本包含了QQ、微信、抖音、微博四个登录的,我只修了QQ的,但是好像没有修好。

主要问题:
1、移动端无法使用
2、无法获取QQ用户头像
3、无法获取QQ用户昵称

链接:http://www.seoyh.net/wp-login.php

按钮在验证码右边那个小小的,没有写样式

文件结构:
user
-----qq
-----qq.php

-----qq/settings.php
-----qq/class-qq.php

qq.php代码

<?php
if (!defined('ABSPATH')) die('Invalid request.');

// 定义常量
define('MARK_SOCIAL_SSO_VERION', "1.0.0");
define('MARK_SOCIAL_SSO_THEME_URL', get_template_directory_uri());
define('MARK_SOCIAL_SSO_THEME_DIR', get_template_directory());

// 引入相关文件
require_once MARK_SOCIAL_SSO_THEME_DIR . "/inc/user/qq/settings.php";
// require_once MARK_SOCIAL_SSO_THEME_DIR . "/inc/user/qq/class-basic.php";
// require_once MARK_SOCIAL_SSO_THEME_DIR . "/inc/user/qq/class-dy.php";
// require_once MARK_SOCIAL_SSO_THEME_DIR . "/inc/user/qq/class-wx.php";
require_once MARK_SOCIAL_SSO_THEME_DIR . "/inc/user/qq/class-qq.php";
// require_once MARK_SOCIAL_SSO_THEME_DIR . "/inc/user/qq/class-wb.php";

// 将样式和脚本加入队列
function mksso_login_enqueue_scripts() {
    wp_enqueue_style('mksso-css', MARK_SOCIAL_SSO_THEME_URL . '/mksso.css', ['login'], MARK_SOCIAL_SSO_VERION);
}
add_action('login_enqueue_scripts', 'mksso_login_enqueue_scripts');

// 处理自定义请求
add_action("parse_request", 'handle_custom_login');
function handle_custom_login(){
	$mksso = isset($_GET['mksso']) ? sanitize_text_field($_GET['mksso']) : false;
	if(in_array( $mksso, ['wx', 'qq', 'wb', 'dy'] )){
		$code = isset($_GET['code']) ? sanitize_text_field($_GET['code']) : false;
		$state = isset($_GET['state']) ? sanitize_text_field($_GET['state']) : false;	
		if($code){
			if( $mksso == 'wb'){
				$check = true;

			}else if( $state ){
				$check = wp_verify_nonce($state,  $mksso . "nonce" );
			}else{
				wp_die("access Denied", "error");
			}
			
			if($check){
				$model = "OAuth_".strtoupper( $mksso );
				$app   = get_option('mksso_'.$mksso);
				
				$model = new $model( $app['appid'], $app['secret'] );
				
				$res   = $model->code_to_openid($code);
				
				
				
				if(is_wp_error($res)) wp_die('登陆失败,请重试');
				
				if($res && isset($res['user']) ){
				    
					$user_id = $res['user'];

					if(is_numeric($user_id) && $user_id){
						$user = get_user_by('ID', $user_id );
						wp_set_current_user( $user_id , $user->user_login);
					    wp_set_auth_cookie( $user_id );
					    do_action( 'wp_login', $user->user_login );
						wp_safe_redirect(  admin_url() );
						exit;
					}else{
						wp_die('登陆失败了', '错误提示');	
					}
					
				}else{
					wp_die('登陆失败', '错误提示');
				}
			}
			exit;	
		}
	}
}


function mark_social_sso_request($url, $args = []){
	
	if( isset($args['body']) && !empty($args['body']) ){
		$res = wp_remote_post($url, $args);
	}else{
		$res = wp_remote_request($url, $args);
	}
	
	if(is_wp_error( $res )) 
	    return $res;

	$body = wp_remote_retrieve_body($res);
	$data = json_decode($body, true);
	return $data;	
}


// 在登录表单上输出自定义登录链接
add_action('login_form', 'custom_login_form_output');
function custom_login_form_output(){

	$cb_url   = urlencode(admin_url());
	$app_dy = get_option('mksso_dy');
	$dy_state = wp_create_nonce('dynonce');
	$dy_url = 'https://open.douyin.com/platform/oauth/connect?client_key='.$app_dy['appid'].'&response_type=code&scope=user_info&state='.$dy_state.'&redirect_uri='. $cb_url;

	$app_qq = get_option('mksso_qq');
	$qq_state = wp_create_nonce('qqnonce');
	$qq_url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&state='.$qq_state. '&client_id='.$app_qq['appid'].'&redirect_uri='.home_url('/?mksso=qq');
	
///.'&state='.$qq_state.'&scope=get_user_info'
	$app_wx = get_option('mksso_wx');
	$wx_state = wp_create_nonce('wxnonce');
	$wx_url = 'https://open.weixin.qq.com/connect/qrconnect?appid='.$app_wx['appid'].'&redirect_uri='.$cb_url.'&response_type=code&scope=SCOPE&state='.$wx_state.'#wechat_redirect';

	$app_wb = get_option('mksso_wb');
	$wb_state = wp_create_nonce('wbnonce');
	$cb_url = home_url().'/?mksso=wb';
	$wb_url = 'https://api.weibo.com/oauth2/authorize?client_id='.$app_wb['appid'].'&response_type=code&redirect_uri='.$cb_url;

	$str = '<div class="sso-items">';
	
	if(isset($app_wx['status']) && $app_wx['status'] )
		$str .= '<div class="sso-item sso-weixin"><a href="'.$wx_url.'">微信登陆</a></div>';
	
	if(isset($app_qq['status']) && $app_qq['status'] )
		$str .= '<div class="sso-item sso-qq"><a href="'.$qq_url.'">QQ登陆</a></div>';
	
	if(isset($app_wb['status']) && $app_wb['status'] )
		$str .= '<div class="sso-item sso-weibo"><a href="'.$wb_url.'">微博登陆</a></div>';
	
	if(isset($app_dy['status']) && $app_dy['status'] )
		$str .= '<div class="sso-item sso-douyin"><a href="'.$dy_url.'">抖音登陆</a></div>';
	
	$str .= '</div>';

	echo $str;
}


    function qqlogin() {
        $app_qq = get_option('mksso_qq');
    	$qq_state = wp_create_nonce('qqnonce');
    	$qq_url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&state='.$qq_state. '&client_id='.$app_qq['appid'].'&redirect_uri='.home_url('/?mksso=qq');
    	echo $qq_url;
    }


settings.php代码


<?php 
add_action("admin_menu", function() {
    add_submenu_page(
        "options-general.php",
        "新媒体登陆设置",
        "新媒体登陆设置",
        "manage_options",
        "mark-social-sso",
        "mark_social_sso_options_page"
    );
});

// 回调函数用于显示设置页面的HTML
function mark_social_sso_options_page() {
    ?>
    <div class="wrap mark-social-sso-page">
        <h1 class="wp-heading-inline">社交媒体登陆设置</h1>
        <hr class="wp-header-end">
        <form method="post" action="options.php">
            <?php
            settings_fields("mark_social_setting_group");
            do_settings_sections("mksso");
            ?>
            <p class="submit">
                <input name="submit" type="submit" id="submit" class="button-primary" value="<?php _e("Save Changes"); ?>" />
            </p>
        </form>
    </div>
    <?php
}

// 用于输出特定设置部分的回调
function section_callback($args) {
    $allowed_tags = [
        'a' => ['href' => [], 'title' => [], 'target' => []],
        'span' => [],
        'pre' => []
    ];
    $cb = home_url("/?mksso=");
    $url = $cb . str_replace("section_id_", "", $args['id']);
    $settings_links = [
        'section_id_qq' => 'https://connect.qq.com/',
        'section_id_wx' => 'https://open.weixin.qq.com/',
        'section_id_dy' => 'https://open.douyin.com/platform/doc/m-2-1-1',
        'section_id_wb' => 'https://open.weibo.com/authentication'
    ];
    
    if (array_key_exists($args['id'], $settings_links)) {
        echo wp_kses('设置教程<a target="_blank" href="'.$settings_links[$args['id']].'">点击这里</a>, 回调链接为:<span><pre>'.$url.'</pre></span>', $allowed_tags);
    }
}


add_action( 'admin_init', 'plugin_admin_init' );
function plugin_admin_init() {
     $list = [
        'wx' => '微信',
        'qq' => 'QQ',
        'wb' => '微博',
        'dy' => '抖音'
     ];
    foreach ($list as $app_key => $app_val) {
        $status = 'mksso_' .$app_key .'[status]';
        $appid = 'mksso_' .$app_key .'[appid]';
        $secret = 'mksso_' .$app_key .'[secret]';
        $section_id = 'section_id_'.$app_key;

        register_setting('mark_social_setting_group', 'mksso_'. $app_key);
        add_settings_section($section_id , $app_val.'登陆', 'section_callback', 'mksso');
        add_settings_field($status, '是否启用', 'mark_sso_field_cb', 'mksso', $section_id , array( 'id' => 'status', 'type' => 'checkbox',  'key' => $app_key));
        add_settings_field($appid, $app_val.'应用ID (APP ID)', 'mark_sso_field_cb', 'mksso', $section_id , array( 'id' => 'appid', 'type' => 'text',  'key' => $app_key));
        add_settings_field($secret, $app_val.'密匙App Secret', 'mark_sso_field_cb', 'mksso', $section_id , array( 'id' => 'secret', 'type' => 'password', 'key' => $app_key)); 
    }
}


// 输出设置字段的回调
function mark_sso_field_cb($args) {
    $options = get_option('mksso_' . $args['key']);
    $field_value = isset($options[$args['id']]) ? $options[$args['id']] : '';
  
    $name = 'mksso_' . $args['key'] . '[' . $args['id'] . ']';
    $checked = $args['type'] === 'checkbox' ? checked(1, $field_value, false) : '';
    
    printf(
        '<input type="%1$s" class="regular-text" name="%2$s" value="%3$s" %4$s>',
        $args['type'],
        $name,
        esc_attr($field_value),
        $checked
    );
}

class-qq.php代码

<?php 
if ( ! defined( 'ABSPATH' ) )  die( 'Invalid request.' );

/**
 * Oauth(OAuth认证)
 */
class OAuth_QQ
{
	private string $appid;  // QQ开放平台应用的AppID

	public const ACCESS_TOKEN = 'https://graph.qq.com/oauth2.0/token';  // 获取访问令牌的URL
	public const USER_OPENID = 'https://graph.qq.com/oauth2.0/me';     // 获取用户OpenID的URL
	public const USER_INFO = 'https://graph.qq.com/user/get_user_info';  // 获取用户信息的URL

    public function __construct(string $appid, string $secret)
    {
        $this->appid = $appid;  
        $this->secret = $secret;
    }

	// 将授权码转换为用户的OpenID
	public function code_to_openid($code){
		$args = [
			'client_id'    => $this->appid,
			'client_secret' => $this->secret,
			'code'          => $code,
			'fmt' => 'json',
			'redirect_uri'  => urlencode(home_url('/?mksso=qq')) ,
			'grant_type'    => 'authorization_code',
			'need_openid' => 1,
			
		];

		$url = add_query_arg($args, self::ACCESS_TOKEN); // 向获取访问令牌的URL发送请求
        $data = mark_social_sso_request($url); // 更改请求函数名称以匹配主题函数名称
        
		if(is_wp_error( $data )) return $data; // 如果获取数据时发生错误,则返回错误对象

		if( isset( $data['access_token'], $data['expires_in'] ) ){
		    
		    $args =  [
		        'access_token' => $data['access_token'],
		        'oauth_consumer_key' => $this->appid,
		        'openid' => $data['openid'],
		        'fmt' => 'json'
		    ];
		    
		    $user_info_url = add_query_arg(
		       $args , self::USER_OPENID); // 构建获取用户OpenID的URL
		    
		    $user = wp_remote_get( $user_info_url ); // 向QQ服务器发送获取用户OpenID的请求
		    $user = wp_remote_retrieve_body( $user ); // 获取请求的响应体中的内容
		    
		    $user = json_decode( $user, true ); // 解析响应体中的JSON数据为关联数组
		    
			if(is_wp_error($user)) return $user; // 如果获取用户OpenID时发生错误,则返回错误对象
			if( !isset($user['openid']) || empty($user['openid']) )
				return new WP_Error('get_openid_fail', '无法返回用户信息'); // 如果用户OpenID不存在或为空,则返回错误对象
			
			// 将用户访问令牌和刷新令牌以及用户OpenID存储到缓存中,有效期为令牌的过期时间
			wp_cache_set( $data['access_token'] , $user['openid'], 'qq_access_token_list', $data['expires_in'] );
			wp_cache_set( $data['access_token'] , $data['refresh_token'], 'qq_access_token_list', $data['expires_in'] );
			wp_cache_set( $user['openid'] , $data['access_token'], 'qq_access_token_list', $data['expires_in'] );
			wp_cache_set( $user['openid'] , $data['refresh_token'], 'qq_access_token_list', $data['expires_in'] );
            
            get_transient( $user['openid'],  'qq_access_token_list', $data['expires_in'] );
			
			// 查询是否已存在使用QQ登录的用户,若不存在则创建新用户
			$wp_user_query = new WP_User_Query( [
				'meta_key' => 'qq_openid',
				'meta_value' => $user['openid']
			] );
			$users = $wp_user_query->get_results();
			if( count( $users ) == 0 ) {
				$userArr = [
					'username' => 'QQ_' . $user['openid'],
					'password' => wp_generate_password(),
				];
				$user_id = wp_create_user( $userArr['username'], $userArr['password']  );
				if( is_numeric( $user_id  )) {
					update_user_meta( $user_id, 'qq_openid', $user['openid'] );
				}
			}else {
				$u = $users[0];
				$user_id = $u->ID;
			}
			
			// 构建返回的数据数组
			$data = [
				'expires_in'	=> $data['expires_in'],
				'access_token'	=> $data['access_token'],
				'openid'		=> $user['openid'],
				'user' => $user_id,
				'refresh_token'	=> $data['refresh_token'],
			];
			
			return $data; // 返回数据数组

		}else{
			return new WP_Error( $data['code'], $data['msg'] ); // 如果请求数据中不包含访问令牌和过期时间,则返回错误对象
		}
	}

    // 获取用户信息
    public function get_user_info($openid) {
        // 获取缓存中的访问令牌列表
        $access_token_list = get_transient("qq_access_token_list");
        if ($access_token_list === false || !isset($access_token_list[$openid])) {
            return new WP_Error('no_access_token', 'No access token found for the given openid');
        }
    
        $access_token = $access_token_list[$openid]; // 获取特定openid的访问令牌
    
        // 构建获取用户信息的URL
        $args = [
            'access_token' => $access_token,
            'oauth_consumer_key' => $this->appid,
            'openid' => $openid
        ];
    
        $url = add_query_arg($args, self::USER_INFO);
        $data = mark_social_sso_request($url); // 发送请求
    
        if (is_wp_error($data)) {
            return $data;
        }
    
        if (is_array($data) && isset($data['ret']) && $data['ret'] == 0) {
            return [
                'openid' => $openid,
                'nickname' => $data['nickname'],
                'avatar' => $data['figureurl_qq_1']
            ];
        } else {
            return new WP_Error('api_error', 'Failed to retrieve user information from QQ', $data);
        }
    }
    
    }

】这样

Your access_token is stored using wp_cache_set, then you use transients to retrieve it later. It’s surprising that you’re able to get it at all.

This code clearly was written by AI, it’s full of errors.

  • access_token should be bound to user data via user_meta—what’s the point of storing it in cache and transients?
  • The same goes for openid: this ID identifies which QQ account it belongs to.
  • You can ignore the refresh_token entirely.

Fix all of the above issues first, then print out the response from the get_user_info function to check.

So awkward, can I just say I still haven’t gotten it done? :rofl:

By the way, could we customize a paid plugin?
It uses points to unlock hidden content
using Discourse’s built-in gamification plugin.

I don’t do Ruby development, so I can’t take this on. My apologies.