liuq 1 місяць тому
батько
коміт
82d6258b49

+ 392 - 0
frontend/public/docs/redirect_sso.md

@@ -0,0 +1,392 @@
+# 快速对接指南 (Redirect SSO)
+
+## 1. 概述
+这是最简单、最快速的集成方式。您无需开发前端登录页面,只需将用户重定向到统一认证平台,待用户登录后,平台会将携带票据 (Ticket) 的用户重定向回您的系统。
+
+## 2. 核心流程
+1. **用户访问**: 用户访问您的应用 (未登录)。
+2. **跳转登录**: 应用重定向用户到 UAP 登录页面 (`?app_id=xxx`)。
+3. **用户登录**: 用户在 UAP 完成认证。
+4. **回调应用**: UAP 重定向回您的应用回调地址 (`?ticket=xxx`)。
+5. **验证票据**: 应用后端调用接口验证 Ticket。
+6. **登录成功**: 验证通过,应用创建自身会话。
+
+## 3. 详细集成步骤
+
+### 第一步:准备工作
+- 在 UAP 管理后台创建应用。
+- **关键**:在应用配置中填写入合法的 **回调地址 (Redirect URIs)**。例如:`http://your-app.com/callback`
+- 获取 `App ID` 和 `App Secret` (用于后端验证 Ticket)。
+
+### 第二步:拼接登录链接 (前端)
+在您的应用中,检测到用户未登录时,直接跳转到以下地址:
+
+**PC 端**:
+`{{API_BASE_URL}}/login?app_id=YOUR_APP_ID`
+
+**移动端 (H5)**:
+`{{API_BASE_URL}}/mobile/login?app_id=YOUR_APP_ID`
+
+> **提示**: 请将 `YOUR_APP_ID` 替换为您实际的应用 ID。
+
+### 第三步:实现回调接口 (后端)
+用户登录成功后,浏览器会跳转到您配置的回调地址,URL 格式如下:
+`http://your-app.com/callback?ticket=TICKET-xxxxx`
+
+您的后端需要接收 `ticket`,并调用 UAP 的验证接口换取用户信息。
+
+**接口地址**: `POST {{API_BASE_URL}}/simple/validate`
+
+**请求参数 (JSON)**:
+| 字段 | 类型 | 必填 | 说明 |
+|---|---|---|---|
+| `app_id` | string | 是 | 您的应用 ID |
+| `ticket` | string | 是 | 接收到的票据 |
+| `timestamp` | int | 是 | 当前时间戳 |
+| `sign` | string | 是 | 签名 |
+
+## 4. 代码示例
+
+### Python
+```python
+import requests
+import time
+import hmac
+import hashlib
+import json
+
+def generate_signature(secret: str, params: dict) -> str:
+    # 1. 过滤掉空值和 sign 字段
+    data = {k: v for k, v in params.items() if k != "sign" and v is not None}
+    # 2. 排序 key
+    sorted_keys = sorted(data.keys())
+    # 3. 拼接字符串
+    query_string = "&".join([f"{k}={data[k]}" for k in sorted_keys])
+    # 4. 计算 HMAC-SHA256
+    signature = hmac.new(
+        secret.encode('utf-8'),
+        query_string.encode('utf-8'),
+        hashlib.sha256
+    ).hexdigest()
+    return signature
+
+def validate_ticket(ticket):
+    # API 地址通常为 /api/v1/simple/validate
+    url = "{{API_BASE_URL}}/simple/validate"
+    app_secret = "YOUR_APP_SECRET" # 务必保密
+
+    payload = {
+        "app_id": "YOUR_APP_ID",
+        "ticket": ticket,
+        "timestamp": int(time.time())
+    }
+    # 使用您的 App Secret 计算签名
+    payload["sign"] = generate_signature(app_secret, payload)
+    
+    try:
+        resp = requests.post(url, json=payload)
+        return resp.json()
+    except Exception as e:
+        print("Error:", e)
+        return None
+```
+
+### Java
+```java
+// 依赖建议: OkHttp 或 Apache HttpClient, FastJson/Jackson
+import okhttp3.*;
+import com.alibaba.fastjson.JSON;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class TicketValidator {
+    public static final String APP_ID = "YOUR_APP_ID";
+    public static final String APP_SECRET = "YOUR_APP_SECRET";
+    public static final String API_URL = "{{API_BASE_URL}}/simple/validate";
+
+    public static String generateSign(Map<String, String> params, String secret) {
+        try {
+            List<String> sortedKeys = new ArrayList<>(params.keySet());
+            Collections.sort(sortedKeys);
+            
+            StringBuilder sb = new StringBuilder();
+            for (String key : sortedKeys) {
+                if (!key.equals("sign") && params.get(key) != null) {
+                    if (sb.length() > 0) sb.append("&");
+                    sb.append(key).append("=").append(params.get(key));
+                }
+            }
+            
+            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
+            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+            sha256_HMAC.init(secret_key);
+            
+            byte[] bytes = sha256_HMAC.doFinal(sb.toString().getBytes(StandardCharsets.UTF_8));
+            StringBuilder hex = new StringBuilder();
+            for (byte b : bytes) hex.append(String.format("%02x", b));
+            return hex.toString();
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
+    public static void validate(String ticket) {
+        try {
+            long timestamp = System.currentTimeMillis() / 1000;
+            
+            Map<String, String> params = new HashMap<>();
+            params.put("app_id", APP_ID);
+            params.put("ticket", ticket);
+            params.put("timestamp", String.valueOf(timestamp));
+            
+            String sign = generateSign(params, APP_SECRET); 
+            params.put("sign", sign);
+            
+            OkHttpClient client = new OkHttpClient();
+            RequestBody body = RequestBody.create(
+                MediaType.parse("application/json; charset=utf-8"), 
+                JSON.toJSONString(params)
+            );
+            
+            Request request = new Request.Builder()
+                .url(API_URL)
+                .post(body)
+                .build();
+                
+            Response response = client.newCall(request).execute();
+            System.out.println(response.body().string());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
+```
+
+### Kotlin
+```kotlin
+import okhttp3.*
+import com.google.gson.Gson
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
+import java.io.IOException
+import java.util.*
+
+object AuthUtils {
+    fun generateSign(params: Map<String, String>, secret: String): String {
+        val sortedKeys = params.keys.filter { it != "sign" }.sorted()
+        val queryString = sortedKeys.joinToString("&") { "${it}=${params[it]}" }
+        
+        val hmacSha256 = "HmacSHA256"
+        val secretKeySpec = SecretKeySpec(secret.toByteArray(Charsets.UTF_8), hmacSha256)
+        val mac = Mac.getInstance(hmacSha256)
+        mac.init(secretKeySpec)
+        
+        return mac.doFinal(queryString.toByteArray(Charsets.UTF_8))
+            .joinToString("") { "%02x".format(it) }
+    }
+}
+
+fun validateTicket(ticket: String) {
+    val appId = "YOUR_APP_ID"
+    val appSecret = "YOUR_APP_SECRET"
+    val url = "{{API_BASE_URL}}/simple/validate"
+    
+    val timestamp = System.currentTimeMillis() / 1000
+    val params = mutableMapOf(
+        "app_id" to appId,
+        "ticket" to ticket,
+        "timestamp" to timestamp.toString()
+    )
+    
+    params["sign"] = AuthUtils.generateSign(params, appSecret)
+    
+    val client = OkHttpClient()
+    val jsonBody = Gson().toJson(params)
+    val body = RequestBody.create(MediaType.parse("application/json"), jsonBody)
+    
+    val request = Request.Builder()
+        .url(url)
+        .post(body)
+        .build()
+        
+    client.newCall(request).enqueue(object : Callback {
+        override fun onFailure(call: Call, e: IOException) {
+            e.printStackTrace()
+        }
+
+        override fun onResponse(call: Call, response: Response) {
+            println(response.body()?.string())
+        }
+    })
+}
+```
+
+### Node.js
+```javascript
+const axios = require('axios');
+const crypto = require('crypto');
+
+const APP_SECRET = 'YOUR_APP_SECRET'; // 务必保密
+
+function getSign(params) {
+  const keys = Object.keys(params)
+    .filter(k => k !== 'sign' && params[k] !== undefined)
+    .sort();
+  const queryString = keys.map(k => `${k}=${params[k]}`).join('&');
+  return crypto.createHmac('sha256', APP_SECRET)
+    .update(queryString)
+    .digest('hex');
+}
+
+async function validateTicket(ticket) {
+  const url = '{{API_BASE_URL}}/simple/validate';
+  const payload = {
+    app_id: 'YOUR_APP_ID',
+    ticket: ticket,
+    timestamp: Math.floor(Date.now() / 1000)
+  };
+  
+  // 计算签名
+  payload.sign = getSign(payload);
+  
+  try {
+    const res = await axios.post(url, payload);
+    console.log('Validation Result:', res.data);
+    return res.data;
+  } catch (error) {
+    console.error('Validation Failed:', error.response?.data || error.message);
+  }
+}
+```
+
+### Go
+```go
+package main
+
+import (
+    "bytes"
+    "crypto/hmac"
+    "crypto/sha256"
+    "encoding/hex"
+    "encoding/json"
+    "fmt"
+    "net/http"
+    "sort"
+    "strings"
+    "time"
+)
+
+func GetSign(secret string, params map[string]interface{}) string {
+    var keys []string
+    for k := range params {
+        if k != "sign" {
+            keys = append(keys, k)
+        }
+    }
+    sort.Strings(keys)
+    var parts []string
+    for _, k := range keys {
+        val := fmt.Sprintf("%v", params[k])
+        parts = append(parts, fmt.Sprintf("%s=%s", k, val))
+    }
+    query := strings.Join(parts, "&")
+    h := hmac.New(sha256.New, []byte(secret))
+    h.Write([]byte(query))
+    return hex.EncodeToString(h.Sum(nil))
+}
+
+func ValidateTicket(ticket string) {
+    appId := "YOUR_APP_ID"
+    appSecret := "YOUR_APP_SECRET" 
+    apiUrl := "{{API_BASE_URL}}/simple/validate"
+    
+    params := map[string]interface{}{
+        "app_id":    appId,
+        "ticket":    ticket,
+        "timestamp": time.Now().Unix(),
+    }
+    
+    // 计算签名
+    params["sign"] = GetSign(appSecret, params)
+    
+    jsonData, _ := json.Marshal(params)
+    resp, err := http.Post(apiUrl, "application/json", bytes.NewBuffer(jsonData))
+    if err != nil {
+        fmt.Println("Error:", err)
+        return
+    }
+    defer resp.Body.Close()
+    
+    // 读取响应...
+    var result map[string]interface{}
+    json.NewDecoder(resp.Body).Decode(&result)
+    fmt.Println("Result:", result)
+}
+```
+
+### Swift
+```swift
+import Foundation
+import CommonCrypto
+
+// 注意:需引入 CommonCrypto
+
+func hmac(string: String, key: String) -> String {
+    var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
+    CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), key, key.count, string, string.count, &digest)
+    let data = Data(digest)
+    return data.map { String(format: "%02hhx", $0) }.joined()
+}
+
+func generateSign(secret: String, params: [String: Any]) -> String {
+    let sortedKeys = params.keys.filter { $0 != "sign" }.sorted()
+    let queryParts = sortedKeys.map { key in
+        return "\(key)=\(params[key]!)"
+    }
+    let queryString = queryParts.joined(separator: "&")
+    return hmac(string: queryString, key: secret)
+}
+
+func validateTicket(ticket: String) {
+    let appId = "YOUR_APP_ID"
+    let secret = "YOUR_APP_SECRET"
+    let url = URL(string: "{{API_BASE_URL}}/simple/validate")!
+    
+    var request = URLRequest(url: url)
+    request.httpMethod = "POST"
+    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+    
+    let timestamp = Int(Date().timeIntervalSince1970)
+    var params: [String: Any] = [
+        "app_id": appId,
+        "ticket": ticket,
+        "timestamp": timestamp
+    ]
+    
+    params["sign"] = generateSign(secret: secret, params: params)
+    
+    request.httpBody = try? JSONSerialization.data(withJSONObject: params)
+    
+    let task = URLSession.shared.dataTask(with: request) { data, response, error in
+        if let data = data {
+            if let json = try? JSONSerialization.jsonObject(with: data, options: []) {
+                print(json)
+            }
+        }
+    }
+    task.resume()
+}
+```
+
+## 5. 响应格式 (成功)
+```json
+{
+  "valid": true,
+  "user_id": 1001,
+  "mobile": "13800138000",
+  "mapped_key": "user_zhangsan",
+  "mapped_email": "zhangsan@example.com"
+}
+```

+ 149 - 19
frontend/public/docs/simple_auth.md

@@ -133,13 +133,13 @@ API_BASE = "{{API_BASE_URL}}/simple"
 APP_ID = "test_app_001"
 APP_SECRET = "secret_key_abc123" # 务必保密
 
-def get_sign(params):
+def generate_signature(secret: str, params: dict) -> str:
     # 排除 sign 字段
     data = {k: v for k, v in params.items() if k != "sign"}
     # 排序并拼接
     query_string = "&".join([f"{k}={data[k]}" for k in sorted(data.keys())])
     # HMAC-SHA256
-    return hmac.new(APP_SECRET.encode(), query_string.encode(), hashlib.sha256).hexdigest()
+    return hmac.new(secret.encode('utf-8'), query_string.encode('utf-8'), hashlib.sha256).hexdigest()
 
 def main():
     # === 步骤 1: 登录获取 Ticket ===
@@ -151,7 +151,7 @@ def main():
         "timestamp": login_ts
     }
     # 计算签名
-    login_payload["sign"] = get_sign(login_payload)
+    login_payload["sign"] = generate_signature(APP_SECRET, login_payload)
 
     print(f"1. 正在尝试登录: {login_payload['identifier']} ...")
     resp = requests.post(f"{API_BASE}/login", json=login_payload)
@@ -171,7 +171,7 @@ def main():
         "timestamp": validate_ts
     }
     # 重新计算签名(参数变了,签名必须重算)
-    validate_payload["sign"] = get_sign(validate_payload)
+    validate_payload["sign"] = generate_signature(APP_SECRET, validate_payload)
 
     print(f"\n2. 正在验证 Ticket...")
     v_resp = requests.post(f"{API_BASE}/validate", json=validate_payload)
@@ -189,21 +189,24 @@ if __name__ == "__main__":
 
 ### Java
 ```java
+// 依赖建议: OkHttp 或 Apache HttpClient, FastJson/Jackson
+import okhttp3.*;
+import com.alibaba.fastjson.JSON;
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
-import java.util.*;
 import java.nio.charset.StandardCharsets;
+import java.util.*;
 
 public class AuthExample {
-    private static final String SECRET = "secret_key_abc123";
+    private static final String APP_ID = "test_app_001";
+    private static final String APP_SECRET = "secret_key_abc123";
+    private static final String API_BASE = "{{API_BASE_URL}}/simple";
 
-    public static String generateSign(Map<String, String> params) {
+    public static String generateSign(Map<String, String> params, String secret) {
         try {
-            // 1. 排序
             List<String> sortedKeys = new ArrayList<>(params.keySet());
             Collections.sort(sortedKeys);
             
-            // 2. 拼接
             StringBuilder sb = new StringBuilder();
             for (String key : sortedKeys) {
                 if (!key.equals("sign") && params.get(key) != null) {
@@ -212,35 +215,72 @@ public class AuthExample {
                 }
             }
             
-            // 3. HMAC-SHA256
             Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
-            SecretKeySpec secret_key = new SecretKeySpec(SECRET.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
             sha256_HMAC.init(secret_key);
             
             byte[] bytes = sha256_HMAC.doFinal(sb.toString().getBytes(StandardCharsets.UTF_8));
-            
-            // 4. Hex
             StringBuilder hex = new StringBuilder();
-            for (byte b : bytes) {
-                hex.append(String.format("%02x", b));
-            }
+            for (byte b : bytes) hex.append(String.format("%02x", b));
             return hex.toString();
         } catch (Exception e) {
-            e.printStackTrace();
             return "";
         }
     }
     
     public static void main(String[] args) {
+        // 示例:仅生成签名
         Map<String, String> params = new HashMap<>();
-        params.put("app_id", "test_app_001");
+        params.put("app_id", APP_ID);
         params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
         
-        System.out.println("Sign: " + generateSign(params));
+        System.out.println("Sign: " + generateSign(params, APP_SECRET));
     }
 }
 ```
 
+### Android (Kotlin)
+```kotlin
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
+
+object AuthUtils {
+    private const val APP_SECRET = "secret_key_abc123"
+
+    fun generateSign(params: Map<String, String>): String {
+        // 1. 过滤 & 排序
+        val sortedKeys = params.keys.filter { it != "sign" }.sorted()
+
+        // 2. 拼接
+        val queryString = sortedKeys.joinToString("&") { key ->
+            "$key=${params[key]}"
+        }
+
+        // 3. HMAC-SHA256
+        val hmacSha256 = "HmacSHA256"
+        val secretKeySpec = SecretKeySpec(APP_SECRET.toByteArray(Charsets.UTF_8), hmacSha256)
+        val mac = Mac.getInstance(hmacSha256)
+        mac.init(secretKeySpec)
+        
+        val bytes = mac.doFinal(queryString.toByteArray(Charsets.UTF_8))
+        
+        // 4. Hex
+        return bytes.joinToString("") { "%02x".format(it) }
+    }
+}
+
+// Usage Example
+fun main() {
+    val params = mapOf(
+        "app_id" to "test_app_001",
+        "identifier" to "13800000001",
+        "timestamp" to (System.currentTimeMillis() / 1000).toString()
+    )
+    val sign = AuthUtils.generateSign(params)
+    println("Signature: $sign")
+}
+```
+
 ### JavaScript (Node.js)
 ```javascript
 const crypto = require('crypto');
@@ -287,3 +327,93 @@ async function login() {
 
 login();
 ```
+
+### Go
+```go
+package main
+
+import (
+	"crypto/hmac"
+	"crypto/sha256"
+	"encoding/hex"
+	"fmt"
+	"sort"
+	"strings"
+	"time"
+)
+
+func GetSign(secret string, params map[string]interface{}) string {
+	// 1. 提取 Key
+	var keys []string
+	for k := range params {
+		if k != "sign" {
+			keys = append(keys, k)
+		}
+	}
+	// 2. 排序
+	sort.Strings(keys)
+
+	// 3. 拼接
+	var parts []string
+	for _, k := range keys {
+		val := fmt.Sprintf("%v", params[k])
+		parts = append(parts, fmt.Sprintf("%s=%s", k, val))
+	}
+	query := strings.Join(parts, "&")
+
+	// 4. HMAC-SHA256
+	h := hmac.New(sha256.New, []byte(secret))
+	h.Write([]byte(query))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+func main() {
+    params := map[string]interface{}{
+        "app_id":    "test_app_001",
+        "identifier": "13800000001",
+        "password":   "123456",
+        "timestamp":  time.Now().Unix(),
+    }
+    
+    secret := "secret_key_abc123"
+    sign := GetSign(secret, params)
+    fmt.Printf("Signature: %s\n", sign)
+}
+```
+
+### Swift
+```swift
+import Foundation
+import CommonCrypto
+
+// 注意:需要添加 Bridging Header 引入 CommonCrypto 或直接在 Linux 环境使用
+
+func hmac(string: String, key: String) -> String {
+    var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
+    CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), key, key.count, string, string.count, &digest)
+    let data = Data(digest)
+    return data.map { String(format: "%02hhx", $0) }.joined()
+}
+
+func generateSign(secret: String, params: [String: Any]) -> String {
+    // 1. 过滤 & 排序
+    let sortedKeys = params.keys.filter { $0 != "sign" }.sorted()
+    
+    // 2. 拼接
+    let queryParts = sortedKeys.map { key in
+        return "\(key)=\(params[key]!)"
+    }
+    let queryString = queryParts.joined(separator: "&")
+    
+    // 3. HMAC
+    return hmac(string: queryString, key: secret)
+}
+
+// Usage
+let params: [String: Any] = [
+    "app_id": "test_app_001",
+    "timestamp": Int(Date().timeIntervalSince1970)
+]
+let sign = generateSign(secret: "secret_123", params: params)
+print(sign)
+```

+ 133 - 17
frontend/src/views/Help.vue

@@ -139,21 +139,43 @@
                   <pre>
 import requests
 import time
-# ... 引入前面的签名算法 generate_signature ...
+import hmac
+import hashlib
+
+def generate_signature(secret: str, params: dict) -> str:
+    # 1. 过滤掉空值和 sign 字段
+    data = {k: v for k, v in params.items() if k != "sign" and v is not None}
+    # 2. 排序 key
+    sorted_keys = sorted(data.keys())
+    # 3. 拼接字符串
+    query_string = "&".join([f"{k}={data[k]}" for k in sorted_keys])
+    # 4. 计算 HMAC-SHA256
+    signature = hmac.new(
+        secret.encode('utf-8'),
+        query_string.encode('utf-8'),
+        hashlib.sha256
+    ).hexdigest()
+    return signature
 
 def validate_ticket(ticket):
     # API 地址通常为 /api/v1/simple/validate
     url = "http://your-uap-domain/api/v1/simple/validate"
+    app_secret = "YOUR_APP_SECRET" # 务必保密
+
     payload = {
         "app_id": "YOUR_APP_ID",
         "ticket": ticket,
         "timestamp": int(time.time())
     }
     # 使用您的 App Secret 计算签名
-    payload["sign"] = generate_signature("YOUR_APP_SECRET", payload)
+    payload["sign"] = generate_signature(app_secret, payload)
     
-    resp = requests.post(url, json=payload)
-    return resp.json()
+    try:
+        resp = requests.post(url, json=payload)
+        return resp.json()
+    except Exception as e:
+        print("Error:", e)
+        return None
                   </pre>
                 </div>
               </el-tab-pane>
@@ -164,6 +186,9 @@ def validate_ticket(ticket):
 // 依赖建议: OkHttp 或 Apache HttpClient, FastJson/Jackson
 import okhttp3.*;
 import com.alibaba.fastjson.JSON;
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
 import java.util.*;
 
 public class TicketValidator {
@@ -171,6 +196,32 @@ public class TicketValidator {
     public static final String APP_SECRET = "YOUR_APP_SECRET";
     public static final String API_URL = "http://your-uap-domain/api/v1/simple/validate";
 
+    public static String generateSign(Map&lt;String, String&gt; params, String secret) {
+        try {
+            List&lt;String&gt; sortedKeys = new ArrayList&lt;&gt;(params.keySet());
+            Collections.sort(sortedKeys);
+            
+            StringBuilder sb = new StringBuilder();
+            for (String key : sortedKeys) {
+                if (!key.equals("sign") && params.get(key) != null) {
+                    if (sb.length() > 0) sb.append("&");
+                    sb.append(key).append("=").append(params.get(key));
+                }
+            }
+            
+            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
+            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+            sha256_HMAC.init(secret_key);
+            
+            byte[] bytes = sha256_HMAC.doFinal(sb.toString().getBytes(StandardCharsets.UTF_8));
+            StringBuilder hex = new StringBuilder();
+            for (byte b : bytes) hex.append(String.format("%02x", b));
+            return hex.toString();
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
     public static void validate(String ticket) {
         try {
             long timestamp = System.currentTimeMillis() / 1000;
@@ -180,8 +231,7 @@ public class TicketValidator {
             params.put("ticket", ticket);
             params.put("timestamp", String.valueOf(timestamp));
             
-            // generateSign 参考前文
-            String sign = AuthExample.generateSign(params); 
+            String sign = generateSign(params, APP_SECRET); 
             params.put("sign", sign);
             
             OkHttpClient client = new OkHttpClient();
@@ -211,8 +261,26 @@ public class TicketValidator {
                   <pre>
 import okhttp3.*
 import com.google.gson.Gson
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
+import java.io.IOException
 import java.util.*
 
+object AuthUtils {
+    fun generateSign(params: Map&lt;String, String&gt;, secret: String): String {
+        val sortedKeys = params.keys.filter { it != "sign" }.sorted()
+        val queryString = sortedKeys.joinToString("&") { "${it}=${params[it]}" }
+        
+        val hmacSha256 = "HmacSHA256"
+        val secretKeySpec = SecretKeySpec(secret.toByteArray(Charsets.UTF_8), hmacSha256)
+        val mac = Mac.getInstance(hmacSha256)
+        mac.init(secretKeySpec)
+        
+        return mac.doFinal(queryString.toByteArray(Charsets.UTF_8))
+            .joinToString("") { "%02x".format(it) }
+    }
+}
+
 fun validateTicket(ticket: String) {
     val appId = "YOUR_APP_ID"
     val appSecret = "YOUR_APP_SECRET"
@@ -225,8 +293,7 @@ fun validateTicket(ticket: String) {
         "timestamp" to timestamp.toString()
     )
     
-    // generateSign 参考前文 Kotlin 示例
-    params["sign"] = AuthUtils.generateSign(params)
+    params["sign"] = AuthUtils.generateSign(params, appSecret)
     
     val client = OkHttpClient()
     val jsonBody = Gson().toJson(params)
@@ -255,17 +322,26 @@ fun validateTicket(ticket: String) {
                 <div class="code-block">
                   <pre>
 const axios = require('axios');
-// getSign 参考前文
+const crypto = require('crypto');
+
+const APP_SECRET = 'YOUR_APP_SECRET'; // 务必保密
+
+function getSign(params) {
+  const keys = Object.keys(params)
+    .filter(k => k !== 'sign' && params[k] !== undefined)
+    .sort();
+  const queryString = keys.map(k => `${k}=${params[k]}`).join('&');
+  return crypto.createHmac('sha256', APP_SECRET)
+    .update(queryString)
+    .digest('hex');
+}
 
 async function validateTicket(ticket) {
-  const appId = 'YOUR_APP_ID';
   const url = 'http://your-uap-domain/api/v1/simple/validate';
-  const timestamp = Math.floor(Date.now() / 1000);
-  
   const payload = {
-    app_id: appId,
+    app_id: 'YOUR_APP_ID',
     ticket: ticket,
-    timestamp: timestamp
+    timestamp: Math.floor(Date.now() / 1000)
   };
   
   // 计算签名
@@ -290,17 +366,39 @@ package main
 
 import (
     "bytes"
+    "crypto/hmac"
+    "crypto/sha256"
+    "encoding/hex"
     "encoding/json"
     "fmt"
     "net/http"
+    "sort"
+    "strings"
     "time"
 )
 
-// GetSign 参考前文
+func GetSign(secret string, params map[string]interface{}) string {
+    var keys []string
+    for k := range params {
+        if k != "sign" {
+            keys = append(keys, k)
+        }
+    }
+    sort.Strings(keys)
+    var parts []string
+    for _, k := range keys {
+        val := fmt.Sprintf("%v", params[k])
+        parts = append(parts, fmt.Sprintf("%s=%s", k, val))
+    }
+    query := strings.Join(parts, "&")
+    h := hmac.New(sha256.New, []byte(secret))
+    h.Write([]byte(query))
+    return hex.EncodeToString(h.Sum(nil))
+}
 
 func ValidateTicket(ticket string) {
     appId := "YOUR_APP_ID"
-    appSecret := "YOUR_APP_SECRET" // 用于 GetSign
+    appSecret := "YOUR_APP_SECRET" 
     apiUrl := "http://your-uap-domain/api/v1/simple/validate"
     
     params := map[string]interface{}{
@@ -333,6 +431,25 @@ func ValidateTicket(ticket string) {
                 <div class="code-block">
                   <pre>
 import Foundation
+import CommonCrypto
+
+// 注意:需引入 CommonCrypto
+
+func hmac(string: String, key: String) -> String {
+    var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
+    CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), key, key.count, string, string.count, &digest)
+    let data = Data(digest)
+    return data.map { String(format: "%02hhx", $0) }.joined()
+}
+
+func generateSign(secret: String, params: [String: Any]) -> String {
+    let sortedKeys = params.keys.filter { $0 != "sign" }.sorted()
+    let queryParts = sortedKeys.map { key in
+        return "\(key)=\(params[key]!)"
+    }
+    let queryString = queryParts.joined(separator: "&")
+    return hmac(string: queryString, key: secret)
+}
 
 func validateTicket(ticket: String) {
     let appId = "YOUR_APP_ID"
@@ -350,7 +467,6 @@ func validateTicket(ticket: String) {
         "timestamp": timestamp
     ]
     
-    // generateSign 参考前文
     params["sign"] = generateSign(secret: secret, params: params)
     
     request.httpBody = try? JSONSerialization.data(withJSONObject: params)