本指南适用于需要使用自定义登录页面(而非跳转到认证中心标准页面),并通过后端 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-..."
}
使用短信验证码获取票据 (Ticket)。
POST /sms-loginapplication/jsonRequest Body:
| Field | Type | Required | Description |
|---|---|---|---|
| app_id | string | Yes | 应用 ID |
| mobile | string | Yes | 用户手机号 |
| code | 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 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(secret.encode('utf-8'), query_string.encode('utf-8'), 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"] = generate_signature(APP_SECRET, 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"] = generate_signature(APP_SECRET, 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()
// 依赖建议: 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 AuthExample {
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, 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 main(String[] args) {
// 示例:仅生成签名
Map<String, String> params = new HashMap<>();
params.put("app_id", APP_ID);
params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
System.out.println("Sign: " + generateSign(params, APP_SECRET));
}
}
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")
}
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();
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)
}
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)