125 lines
4.6 KiB
C#
125 lines
4.6 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Security.Cryptography;
|
|||
|
|
using System.Text;
|
|||
|
|
|
|||
|
|
namespace VOL.Core.Utilities
|
|||
|
|
{
|
|||
|
|
public static class SaTokenSignHelper
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 根据业务参数生成 Java Sa-Token Sign 兼容的 QueryString
|
|||
|
|
/// 自动生成 timestamp 和 nonce,如果外部未提供
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="paramMap">业务参数字典(可包含 teacherNo 等)</param>
|
|||
|
|
/// <param name="secretKey">应用密钥</param>
|
|||
|
|
/// <param name="channel">应用编码,例如 sk-yuedong</param>
|
|||
|
|
/// <returns>带 sign 的完整 QueryString</returns>
|
|||
|
|
public static string BuildQueryString(Dictionary<string, object> paramMap, string secretKey)
|
|||
|
|
{
|
|||
|
|
if (paramMap == null)
|
|||
|
|
paramMap = new Dictionary<string, object>();
|
|||
|
|
|
|||
|
|
// 1️⃣ TreeMap 排序(ASCII 升序)
|
|||
|
|
var sorted = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
|||
|
|
foreach (var kv in paramMap)
|
|||
|
|
sorted[kv.Key] = kv.Value.ToString();
|
|||
|
|
|
|||
|
|
// 2️⃣ 拼接签名字符串 k=v&k=v&...&key=secretKey
|
|||
|
|
var sbSign = new StringBuilder();
|
|||
|
|
foreach (var kv in sorted)
|
|||
|
|
sbSign.Append(kv.Key).Append("=").Append(kv.Value).Append("&");
|
|||
|
|
sbSign.Append("key=").Append(secretKey.Trim());
|
|||
|
|
|
|||
|
|
string sign = Md5(sbSign.ToString());
|
|||
|
|
|
|||
|
|
// 3️⃣ 拼接 QueryString(保留外部字典原顺序)
|
|||
|
|
var sbQuery = new StringBuilder();
|
|||
|
|
foreach (var kv in paramMap)
|
|||
|
|
sbQuery.Append(Uri.EscapeDataString(kv.Key))
|
|||
|
|
.Append("=")
|
|||
|
|
.Append(Uri.EscapeDataString(kv.Value.ToString()))
|
|||
|
|
.Append("&");
|
|||
|
|
|
|||
|
|
sbQuery.Append("sign=").Append(sign);
|
|||
|
|
|
|||
|
|
return sbQuery.ToString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 构建 POST JSON 上传接口的 QueryString(包含 sign)
|
|||
|
|
/// ⚠️ 不与 GET 共用,避免误用
|
|||
|
|
/// </summary>
|
|||
|
|
public static string BuildPostJsonQueryString(
|
|||
|
|
Dictionary<string, object> paramMap,
|
|||
|
|
string secretKey,
|
|||
|
|
string jsonBody)
|
|||
|
|
{
|
|||
|
|
if (paramMap == null)
|
|||
|
|
throw new ArgumentNullException(nameof(paramMap));
|
|||
|
|
|
|||
|
|
if (string.IsNullOrWhiteSpace(jsonBody))
|
|||
|
|
throw new ArgumentException("POST JSON body 不能为空");
|
|||
|
|
|
|||
|
|
// 1️⃣ 参与签名的参数(TreeMap + ASCII 排序)
|
|||
|
|
var signParams = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
|||
|
|
|
|||
|
|
foreach (var kv in paramMap)
|
|||
|
|
signParams[kv.Key] = kv.Value.ToString();
|
|||
|
|
|
|||
|
|
// ⭐ POST JSON 特有:整个 body 作为 reqbody
|
|||
|
|
signParams["reqbody"] = jsonBody;
|
|||
|
|
|
|||
|
|
// 2️⃣ 拼接签名字符串
|
|||
|
|
// k=v&k=v&...&key=secretKey
|
|||
|
|
var sbSign = new StringBuilder();
|
|||
|
|
foreach (var kv in signParams)
|
|||
|
|
sbSign.Append(kv.Key)
|
|||
|
|
.Append("=")
|
|||
|
|
.Append(kv.Value)
|
|||
|
|
.Append("&");
|
|||
|
|
|
|||
|
|
sbSign.Append("key=").Append(secretKey.Trim());
|
|||
|
|
|
|||
|
|
string sign = Md5(sbSign.ToString());
|
|||
|
|
|
|||
|
|
// 3️⃣ 构建 URL QueryString(⚠️ 不包含 reqbody)
|
|||
|
|
var sbQuery = new StringBuilder();
|
|||
|
|
foreach (var kv in paramMap)
|
|||
|
|
sbQuery.Append(Uri.EscapeDataString(kv.Key))
|
|||
|
|
.Append("=")
|
|||
|
|
.Append(Uri.EscapeDataString(kv.Value.ToString()))
|
|||
|
|
.Append("&");
|
|||
|
|
|
|||
|
|
sbQuery.Append("sign=").Append(sign);
|
|||
|
|
|
|||
|
|
return sbQuery.ToString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// MD5 小写 32 位
|
|||
|
|
/// </summary>
|
|||
|
|
private static string Md5(string input)
|
|||
|
|
{
|
|||
|
|
using var md5 = MD5.Create();
|
|||
|
|
var bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
|
|||
|
|
var sb = new StringBuilder();
|
|||
|
|
foreach (var b in bytes)
|
|||
|
|
sb.Append(b.ToString("x2"));
|
|||
|
|
return sb.ToString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 生成随机字符串 nonce,可指定长度
|
|||
|
|
/// </summary>
|
|||
|
|
public static string RandomString(int length = 20)
|
|||
|
|
{
|
|||
|
|
const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|||
|
|
var random = new Random();
|
|||
|
|
return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|