这是最简单、最快速的集成方式。您无需开发前端登录页面,只需将用户重定向到统一认证平台,待用户登录后,平台会将携带票据 (Ticket) 的用户重定向回您的系统。
?app_id=xxx)。?ticket=xxx)。http://your-app.com/callbackApp ID 和 App Secret (用于后端验证 Ticket)。在您的应用中,检测到用户未登录时,直接跳转到以下地址:
PC 端:
https://api.hnyunzhu.com/api/v1/login?app_id=YOUR_APP_ID
移动端 (H5):
https://api.hnyunzhu.com/api/v1/mobile/login?app_id=YOUR_APP_ID
提示: 请将
YOUR_APP_ID替换为您实际的应用 ID。
用户登录成功后,浏览器会跳转到您配置的回调地址,URL 格式如下:
http://your-app.com/callback?ticket=TICKET-xxxxx
您的后端需要接收 ticket,并调用 UAP 的验证接口换取用户信息。
接口地址: POST https://api.hnyunzhu.com/api/v1/simple/validate
请求参数 (JSON):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 是 | 您的应用 ID |
| ticket | string | 是 | 接收到的票据 |
| timestamp | int | 是 | 当前时间戳 |
| sign | string | 是 | 签名 |
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 = "https://api.hnyunzhu.com/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(app_secret, payload)
try:
resp = requests.post(url, json=payload)
return resp.json()
except Exception as e:
print("Error:", e)
return None
// 依赖建议: 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 = "https://api.hnyunzhu.com/api/v1/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();
}
}
}
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 = "https://api.hnyunzhu.com/api/v1/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())
}
})
}
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 = 'https://api.hnyunzhu.com/api/v1/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);
}
}
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 := "https://api.hnyunzhu.com/api/v1/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)
}
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: "https://api.hnyunzhu.com/api/v1/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()
}
{
"valid": true,
"user_id": 1001,
"mobile": "13800138000",
"mapped_key": "user_zhangsan",
"mapped_email": "zhangsan@example.com"
}