无二级域名跨域单点登录需求:两个独立WordPress站点实现方案咨询
Alright, let's solve this cross-domain SSO problem for your two independent WordPress sites. Since they're on separate domains, servers, and databases, subdomain-based tricks won't work here—we need a token-driven approach that syncs user data and handles seamless login across both sites. Here's how to implement it while keeping your domain1 registration form mostly untouched:
We'll use server-side token authentication paired with WordPress hooks to:
- Sync new users from domain1 to domain2 automatically when they register
- Generate a secure token when users log into domain1, then redirect them to domain2 to validate the token and auto-login
- Keep user data (like passwords, profiles) in sync between both sites
First, add a strong, unique shared key to both sites' wp-config.php files—this will validate requests between the two sites to prevent unauthorized access:
// Add this to wp-config.php on BOTH domain1.com and domain2.com define('SSO_SHARED_KEY', 'your_32+_character_random_secure_key_here');
Make sure this key is long, random, and never shared publicly.
We'll use WordPress's user_register hook to push new user data to domain2 right after they sign up on domain1—no need to modify your existing registration form.
Add this code to domain1's functions.php:
// Sync new domain1 users to domain2 add_action('user_register', 'sync_new_user_to_domain2', 10, 1); function sync_new_user_to_domain2($user_id) { $user = get_user_by('id', $user_id); $sync_payload = array( 'action' => 'sso_create_user', 'username' => $user->user_login, 'email' => $user->user_email, 'password_hash' => $user->user_pass, // Pass WordPress's native password hash 'nickname' => $user->nickname, 'secret_key' => SSO_SHARED_KEY ); // Send request to domain2's AJAX endpoint $response = wp_remote_post('https://domain2.com/wp-admin/admin-ajax.php', array( 'method' => 'POST', 'body' => $sync_payload, 'sslverify' => true // Keep this true if domain2 has a valid SSL certificate )); // Log errors for debugging (optional but recommended) if (is_wp_error($response)) { error_log("SSO Sync Failed: " . $response->get_error_message()); } }
Then add this code to domain2's functions.php to receive and create the synced user:
// Domain2 endpoint to create synced users from domain1 add_action('wp_ajax_sso_create_user', 'sso_receive_synced_user'); add_action('wp_ajax_nopriv_sso_create_user', 'sso_receive_synced_user'); function sso_receive_synced_user() { // Validate shared key first if (!isset($_POST['secret_key']) || $_POST['secret_key'] !== SSO_SHARED_KEY) { wp_send_json_error('Invalid security key'); exit; } $username = sanitize_user($_POST['username']); $email = sanitize_email($_POST['email']); $password_hash = $_POST['password_hash']; // Check if user already exists if (username_exists($username) || email_exists($email)) { wp_send_json_success('User already exists'); exit; } // Create user with the same password hash as domain1 $user_id = wp_create_user($username, $password_hash, $email); if (is_wp_error($user_id)) { wp_send_json_error('Failed to create user: ' . $user_id->get_error_message()); exit; } // Sync additional profile data wp_update_user(array( 'ID' => $user_id, 'nickname' => sanitize_text_field($_POST['nickname']), 'display_name' => sanitize_text_field($_POST['nickname']) )); wp_send_json_success('User synced successfully'); exit; }
When a user logs into domain1, we'll generate an encrypted token with their user data, then redirect them to domain2 to auto-login.
Add this to domain1's functions.php:
// Redirect domain1 users to domain2 after login with SSO token add_action('wp_login', 'sso_redirect_to_domain2', 10, 2); function sso_redirect_to_domain2($user_login, $user) { // Token expires in 10 minutes (adjust as needed) $expiry = time() + 600; $token_data = json_encode(array( 'user_email' => $user->user_email, 'expiry' => $expiry )); // Encrypt token with shared key $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc')); $encrypted_token = openssl_encrypt($token_data, 'aes-256-cbc', SSO_SHARED_KEY, 0, $iv); $encoded_token = base64_encode($encrypted_token . '::' . $iv); // Redirect to domain2's SSO verification page $redirect_url = 'https://domain2.com/sso-verify/?token=' . urlencode($encoded_token); wp_redirect($redirect_url); exit; }
Create a new page on domain2 with the slug sso-verify, then use this custom template to validate the token and auto-login the user:
Create a file named page-sso-verify.php in domain2's theme folder:
<?php /* Template Name: SSO Verification Page */ // Check for valid token if (!isset($_GET['token'])) { wp_redirect(home_url('/login/?error=no_token')); exit; } $encoded_token = urldecode($_GET['token']); list($encrypted_data, $iv) = explode('::', base64_decode($encoded_token), 2); // Decrypt token $decrypted_data = openssl_decrypt($encrypted_data, 'aes-256-cbc', SSO_SHARED_KEY, 0, $iv); $token_data = json_decode($decrypted_data, true); // Check if token is expired if (time() > $token_data['expiry']) { wp_redirect(home_url('/login/?error=token_expired')); exit; } // Find user by email $user = get_user_by('email', $token_data['user_email']); if (!$user) { // Fallback: Fetch user data from domain1 if sync failed earlier $fetch_payload = array( 'action' => 'sso_get_user_data', 'user_email' => $token_data['user_email'], 'secret_key' => SSO_SHARED_KEY ); $response = wp_remote_post('https://domain1.com/wp-admin/admin-ajax.php', array( 'method' => 'POST', 'body' => $fetch_payload, 'sslverify' => true )); if (!is_wp_error($response) && $response['response']['code'] == 200) { $user_data = json_decode($response['body'], true); $user_id = wp_create_user($user_data['username'], $user_data['password_hash'], $user_data['email']); $user = get_user_by('id', $user_id); } else { wp_redirect(home_url('/login/?error=user_not_found')); exit; } } // Auto-login the user wp_set_auth_cookie($user->ID, true); // "true" enables "remember me" wp_set_current_user($user->ID); // Redirect to domain2's homepage or user dashboard wp_redirect(home_url()); exit; ?>
Add this fallback endpoint to domain1's functions.php to let domain2 fetch user data if sync failed:
// Domain1 endpoint to fetch user data for SSO fallback add_action('wp_ajax_sso_get_user_data', 'sso_provide_user_data'); add_action('wp_ajax_nopriv_sso_get_user_data', 'sso_provide_user_data'); function sso_provide_user_data() { if (!isset($_POST['secret_key']) || $_POST['secret_key'] !== SSO_SHARED_KEY) { wp_send_json_error('Invalid security key'); exit; } $user = get_user_by('email', sanitize_email($_POST['user_email'])); if (!$user) { wp_send_json_error('User not found'); exit; } wp_send_json_success(array( 'username' => $user->user_login, 'email' => $user->user_email, 'password_hash' => $user->user_pass, 'nickname' => $user->nickname )); exit; }
To keep passwords in sync when users change them on either site, add this hook to domain1's functions.php:
// Sync password changes from domain1 to domain2 add_action('password_reset', 'sync_password_change_to_domain2', 10, 2); function sync_password_change_to_domain2($user, $new_pass) { $sync_payload = array( 'action' => 'sso_update_password', 'user_email' => $user->user_email, 'new_password_hash' => wp_hash_password($new_pass), 'secret_key' => SSO_SHARED_KEY ); wp_remote_post('https://domain2.com/wp-admin/admin-ajax.php', array( 'method' => 'POST', 'body' => $sync_payload, 'sslverify' => true )); }
And this to domain2's functions.php:
// Domain2 endpoint to update synced user passwords add_action('wp_ajax_sso_update_password', 'sso_receive_password_update'); add_action('wp_ajax_nopriv_sso_update_password', 'sso_receive_password_update'); function sso_receive_password_update() { if (!isset($_POST['secret_key']) || $_POST['secret_key'] !== SSO_SHARED_KEY) { wp_send_json_error('Invalid security key'); exit; } $user = get_user_by('email', sanitize_email($_POST['user_email'])); if (!$user) { wp_send_json_error('User not found'); exit; } wp_update_user(array( 'ID' => $user->ID, 'user_pass' => $_POST['new_password_hash'] )); wp_send_json_success('Password updated successfully'); exit; }
- Always use HTTPS: Both sites must have valid SSL certificates to prevent token and data interception.
- Rotate your shared key: Periodically update the
SSO_SHARED_KEYin bothwp-config.phpfiles. - Add logging: Use WordPress's
error_log()function or a logging plugin to track sync/SSO failures. - Limit token expiry: Keep token expiry short (10-15 minutes) to reduce the risk of token misuse.
内容的提问来源于stack exchange,提问作者murcoder

