UCenter(Discuz! 核心用戶中心)實現多應用單點登入(SSO)的核心機制是「中心廣播 + 客戶端回調 + Cookie 共享」。本文拆解完整通信鏈路:從應用登入驗證、UCenter 廣播通知、各應用 uc.php 回調處理、_authcode 加密解密、_setcookie 寫入 Cookie,到跨域同步登入的實現細節。
用戶訪問應用 A (Discuz) 登入
↓
應用 A logging.php → uc_user_login($username, $password) 驗證
↓ 成功
應用 A uc_client/client.php → uc_user_synlogin($uid)
↓
uc_api_post('user', 'synlogin', ['uid'=>$uid]) → HTTP POST 到 UCenter index.php
↓
UCenter index.php 解析 model=user, action=synlogin
↓
UCenter control/user.php → onsynlogin() 遍歷「開啟同步登入」的應用列表
↓
對每個應用輸出 <script src="APP_URL/api/uc.php?time=...&code=..." reload="1"></script>
↓ 瀏覽器執行 JS
應用 B/C/D 的 api/uc.php 接收 GET 參數
↓
uc.php → synlogin() → _authcode($code, 'DECODE', UC_KEY) 解密
↓ 獲得 uid, username 等
_setcookie('sid', $auth, ...) 寫入應用域名下 Cookie
↓
用戶後續訪問應用 B/C/D → 攜帶 Cookie → 自動識別登入狀態
| 檔案 | 角色 | 核心函數 |
|---|---|---|
logging.php | 應用登入入口 | uc_user_login() |
uc_client/client.php | 客戶端 SDK | uc_user_synlogin(), uc_api_post() |
UCenter/index.php | 中心入口/分發 | 路由到 control/user.php |
UCenter/control/user.php | 業務邏輯 | onsynlogin() 廣播 JS |
應用/api/uc.php | 回調入口 | synlogin(), _authcode(), _setcookie() |
<?php
include './config.inc.php';
include './uc_client/client.php';
$uid = uc_user_login('kyee', '123456');
if ($uid > 0) {
setcookie('username', 'kyee', time()+86400);
echo uc_user_synlogin($uid); // 輸出一堆 <script src=...>
echo '登入成功';
}
?>
onsynlogin 簡化)<?php
// UCenter control/user.php
public function onsynlogin($uid) {
$apps = $this->getAppsWithSynlogin(); // 從緩存/DB 讀取
foreach ($apps as $app) {
$code = _authcode("uid=$uid", 'ENCODE', $app['authkey']);
$url = $app['url'] . "/api/uc.php?time=" . time() . "&code=" . urlencode($code);
echo '<script type="text/javascript" src="' . $url . '" reload="1"></script>';
}
}
?>
api/uc.php)<?php
// 應用 api/uc.php
function synlogin($get) {
$code = $get['code'];
$data = _authcode($code, 'DECODE', UC_KEY); // 解密得到 "uid=123"
parse_str($data, $arr);
$uid = $arr['uid'];
// 業務邏輯:查找/建立本地用戶、寫入 Session、設定 Cookie
$user = getUserByUid($uid);
if ($user) {
_setcookie('auth', _authcode($user['id'] . "\t" . $user['password'], 'ENCODE'), 86400);
}
}
?>
_authcode(簡化版)<?php
function _authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
$key = md5($key);
if ($operation == 'DECODE') {
$string = base64_decode($string);
// ... XOR 解密邏輯 ...
} else {
// ... XOR 加密 + base64_encode ...
}
return $result;
}
?>
瀏覽器同源策略禁止跨域寫 Cookie。UCenter 巧妙利用 <script src="跨域URL"> **不受同源策略限制**的特性: