本指南适用于需要使用自定义登录页面(而非跳转到认证中心标准页面),并通过后端 API 直接进行用户认证的场景。
Base URL: {{API_BASE_URL}}/simple (请根据实际部署环境替换)
/login (账号+密码+签名) -> 获取 Ticket。/validate (Ticket+签名) -> 获取用户信息。所有接口(除部分公开接口外)都需要校验签名。
步骤:
sign 本身)。key1=value1&key2=value2... 格式。App Secret 作为密钥,对拼接字符串进行 HMAC-SHA256 计算。import hmac
import hashlib
def generate_signature(secret: str, params: dict) -> str:
data = {k: v for k, v in params.items() if k != "sign" and v is not None}
sorted_keys = sorted(data.keys())
query_string = "&".join([f"{k}={data[k]}" for k in sorted_keys])
signature = hmac.new(
secret.encode('utf-8'),
query_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature
获取临时票据 (Ticket)。
POST /loginapplication/jsonRequest Body:
| Field | Type | Required | Description |
|---|---|---|---|
| app_id | string | Yes | 应用 ID |
| identifier | string | Yes | 用户标识(手机号、用户名或邮箱) |
| password | string | Yes | 明文密码 |
| timestamp | int | Yes | 当前时间戳 (秒) |
| sign | string | Yes | 签名 |
Response (200):
{
"ticket": "TICKET-7f8e9d0a-..."
}
解析票据获取用户信息。
POST /validateapplication/jsonRequest Body:
| Field | Type | Required | Description |
|---|---|---|---|
| app_id | string | Yes | 应用 ID |
| ticket | string | Yes | 上一步获取的票据 |
| timestamp | int | Yes | 当前时间戳 |
| sign | string | Yes | 签名 (参数变化需重新计算) |
Response (200):
{
"valid": true,
"user_id": 1001,
"mobile": "13800138000",
"mapped_key": "user_zhangsan", // 第三方映射ID
"mapped_email": "zhangsan@example.com"
}
Response (Invalid):
{ "valid": false }
import requests
import time
import hmac
import hashlib
import json
# 配置信息
API_BASE = "{{API_BASE_URL}}/simple"
APP_ID = "test_app_001"
APP_SECRET = "secret_key_abc123" # 务必保密
def get_sign(params):
# 排除 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()
def main():
# === 步骤 1: 登录获取 Ticket ===
login_ts = int(time.time())
login_payload = {
"app_id": APP_ID,
"identifier": "13800000001",
"password": "password123",
"timestamp": login_ts
}
# 计算签名
login_payload["sign"] = get_sign(login_payload)
print(f"1. 正在尝试登录: {login_payload['identifier']} ...")
resp = requests.post(f"{API_BASE}/login", json=login_payload)
if resp.status_code != 200:
print(f"登录失败: {resp.text}")
return
ticket = resp.json().get("ticket")
print(f"登录成功! 获取到 Ticket: {ticket}")
# === 步骤 2: 使用 Ticket 换取用户信息 ===
validate_ts = int(time.time())
validate_payload = {
"app_id": APP_ID,
"ticket": ticket,
"timestamp": validate_ts
}
# 重新计算签名(参数变了,签名必须重算)
validate_payload["sign"] = get_sign(validate_payload)
print(f"\n2. 正在验证 Ticket...")
v_resp = requests.post(f"{API_BASE}/validate", json=validate_payload)
user_info = v_resp.json()
if user_info.get("valid"):
print("验证成功! 用户信息如下:")
print(json.dumps(user_info, indent=2, ensure_ascii=False))
else:
print("Ticket 无效或已过期")
if __name__ == "__main__":
main()
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
import java.nio.charset.StandardCharsets;
public class AuthExample {
private static final String SECRET = "secret_key_abc123";
public static String generateSign(Map<String, String> params) {
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) {
if (sb.length() > 0) sb.append("&");
sb.append(key).append("=").append(params.get(key));
}
}
// 3. HMAC-SHA256
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));
// 4. Hex
StringBuilder hex = new StringBuilder();
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("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
System.out.println("Sign: " + generateSign(params));
}
}
const crypto = require('crypto');
const axios = require('axios'); // npm install axios
const APP_ID = 'test_app_001';
const APP_SECRET = 'secret_key_abc123';
const BASE_URL = '{{API_BASE_URL}}/simple';
function getSign(params) {
// 1. 过滤 & 排序
const keys = Object.keys(params)
.filter(k => k !== 'sign' && params[k] !== undefined)
.sort();
// 2. 拼接 Query String
const queryString = keys.map(k => `${k}=${params[k]}`).join('&');
// 3. HMAC-SHA256
return crypto.createHmac('sha256', APP_SECRET)
.update(queryString)
.digest('hex');
}
async function login() {
const timestamp = Math.floor(Date.now() / 1000);
const payload = {
app_id: APP_ID,
identifier: '13800000001',
password: 'password123',
timestamp: timestamp
};
payload.sign = getSign(payload);
try {
console.log('Sending login request...');
const res = await axios.post(`${BASE_URL}/login`, payload);
console.log('Ticket:', res.data.ticket);
} catch (error) {
console.error('Login Failed:', error.response?.data || error.message);
}
}
login();