Discuz! UCenter 內部採用 GBK 編碼,其 authcode 加解密算法對字節序列強依賴。PHP 4/5 對 UTF-8 支持不完善,導致 UTF-8 版本 UCenter 在處理中文用戶名時,authcode 輸出與 Java 等 Unicode 原生語言不兼容,**必須部署 GBK 版 UCenter**,Java 端在調用前將參數轉為 GBK 字節流。
+----------------+ HTTP POST (GBK URL Encoded) +----------------+
| Java 應用 | <--------------------------------> | UCenter (GBK) |
| (UTF-8 內部) | m=user&a=register&input=authcode() | Discuz! 內核 |
+----------------+ 返回: 數字代碼 / JS 同步代碼 +----------------+
GitHub: discuz-ucenter-api-for-java(作者 ping.china),封裝了 authcode、HTTP 通信、參數構建、響應解析。
<dependency>
<groupId>com.github.ping-china</groupId>
<artifactId>ucenter-api-java</artifactId>
<version>1.0.0</version>
</dependency>
UCenter 要求所有輸入參數經 authcode($data, 'ENCODE', $key) 加密後作為 input 參數傳遞。Java 端需精確移植該算法(含 $key 派生、md5 混淆、異或運算、Base64 變體)。SDK 已封裝:
UCenterClient client = new UCenterClient(ucApiUrl, ucAppId, ucKey);
Map params = new HashMap<>();
params.put("username", "張三");
params.put("password", "123456");
params.put("email", "zhangsan@example.com");
String encodedInput = client.authcode(params, "ENCODE"); // 內部自動 GBK 編碼
String postData = "m=user&a=register&release=1.5&inajax=1&appid=3&input=" + URLEncoder.encode(encodedInput, "GBK");
String response = HttpClient.post(ucApiUrl, postData, "GBK"); // 響應也按 GBK 解碼
m:模塊名(user/pm/friend...)a:動作名(register/login/delete...)appid:Java 應用在 UCenter 註冊的應用 IDinajax=1:抑制 PHP 端跳轉輸出,純數據返回UCenter 返回純數字字符串,如 1、-1、-4,需映射業務含義:
| 代碼 | 含義 | 處理策略 |
|---|---|---|
| >0 | 用戶 UID(註冊/登錄成功) | 本地創建/綁定用戶,寫入 Session |
| -1 | 用戶名不合法 | 前端提示「用戶名包含非法字符」 |
| -2 | 包含敏感詞 | 前端提示「用戶名包含敏感詞」 |
| -3 | 用戶名已存在 | 引導登錄或找回密碼 |
| -4 | Email 格式錯誤 | 前端校驗 |
| -5 | Email 不允許註冊 | 檢查 UCenter 設置 |
| -6 | Email 已被註冊 | 引導找回密碼 |
成功登錄/登出後,UCenter 返回一段 <script src=".../api/uc.php?time=...&code=..."></script>,Java 應用**必須直接輸出到頁面**,瀏覽器執行該 JS 會向所有已註冊的應用發送跨域請求,各應用自行處理 Cookie/Session。
// 登錄成功後
String synCode = client.getSynLogin(uid); // 獲取同步登錄 JS 代碼
response.setContentType("text/html;charset=UTF-8");
response.getWriter().println(synCode); // 直接輸出到頁面
註冊、修改資料、修改密碼等**非登錄操作不會自動推送同步**。其他應用需定時或業務觸發調用 uc_user_synlogin / uc_user_synlogout 或查詢 uc_notelist 獲取待同步通知:
// 定時任務:每 5 分鐘拉取一次
List notes = client.getNoteList(lastNoteId);
for (UCenterNote note : notes) {
switch (note.getOperation()) {
case "register" -> syncRegister(note.getData());
case "update" -> syncUpdate(note.getData());
case "delete" -> syncDelete(note.getData());
}
lastNoteId = note.getId();
}
public class UCenterCodec {
private static final Charset GBK = Charset.forName("GBK");
private static final Charset UTF8 = StandardCharsets.UTF_8;
// 內部 UTF-8 字符串 -> GBK 字節數組 -> URLEncoder.encode(GBK)
public static String encodeParam(String utf8Str) {
try {
byte[] gbkBytes = utf8Str.getBytes(GBK);
// 手動構造 %XX 形式,避免 URLEncoder 空格變 + 號
StringBuilder sb = new StringBuilder();
for (byte b : gbkBytes) {
if (isSafe(b)) sb.append((char) b);
else sb.append('%').append(String.format("%02X", b & 0xFF));
}
return sb.toString();
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("GBK not supported", e);
}
}
// 響應 GBK 字節 -> UTF-8 字符串
public static String decodeResponse(byte[] gbkBytes) {
return new String(gbkBytes, GBK);
}
}
authcode 不匹配,**無解,必須重裝 GBK 版**。URLEncoder.encode(str) 默認用平台編碼(Linux 通常 UTF-8),導致傳輸字節錯誤。**強制指定 "GBK"**。UCenter 整合的核心難點在於 GBK 編碼強制要求 與 authcode 算法精確移植。使用成熟 SDK 可屏蔽底層細節,重點關注:參數 GBK 編碼 → POST 請求 → 數字代碼解析 → 同步 JS 代碼輸出 → 異步增量拉取。新架構建議:以 UCenter 為用戶中心,Java 應用作為從屬客戶端,所有用戶狀態變更最終以 UCenter 為準,通過定時拉取保證最終一致性。