200 lines
6.2 KiB
C#
200 lines
6.2 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Dlhis.Basic.Organization.Response;
|
|
using Kurisu.AspNetCore.CustomClass;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace Dlhis.Basic.Organization.Services;
|
|
|
|
public interface IAliyunClient : IScopeDependency
|
|
{
|
|
Task<AliyunId3MetaVerifyResponse> Id3MetaVerifyAsync(
|
|
string identifyNum,
|
|
string userName,
|
|
string faceBase64,
|
|
string crop = "F");
|
|
}
|
|
|
|
public class AliyunClient : IAliyunClient
|
|
{
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
private readonly IConfiguration _configuration;
|
|
private readonly string _accessKeyId;
|
|
private readonly string _accessKeySecret;
|
|
|
|
private const string Endpoint = "cloudauth.aliyuncs.com";
|
|
private const string ApiVersion = "2019-03-07";
|
|
|
|
public AliyunClient(IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger<AliyunClient> logger)
|
|
{
|
|
_httpClientFactory = httpClientFactory;
|
|
_configuration = configuration;
|
|
|
|
_accessKeyId = "LTAI5tAqyWKcBcSvzpc1ZM72";
|
|
_accessKeySecret = "YZMSqsmMGlGZuHw5WU2tqt0mmIBX6h";
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 使用 Base64 图片进行三要素核验
|
|
/// </summary>
|
|
public async Task<AliyunId3MetaVerifyResponse> Id3MetaVerifyAsync(
|
|
string identifyNum,
|
|
string userName,
|
|
string faceBase64,
|
|
string crop = "F")
|
|
{
|
|
if (faceBase64.Contains(","))
|
|
{
|
|
faceBase64 = faceBase64[(faceBase64.IndexOf(',') + 1)..];
|
|
}
|
|
|
|
var parameters = new SortedDictionary<string, string>
|
|
{
|
|
{ "Action", "Id3MetaVerify" },
|
|
{ "Format", "JSON" },
|
|
{ "Version", ApiVersion },
|
|
{ "AccessKeyId", _accessKeyId },
|
|
{ "SignatureMethod", "HMAC-SHA1" },
|
|
{ "SignatureVersion", "1.0" },
|
|
{ "SignatureNonce", Guid.NewGuid().ToString("N") },
|
|
{ "Timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ") },
|
|
{ "ParamType", "normal" },
|
|
{ "IdentifyNum", identifyNum },
|
|
{ "UserName", userName },
|
|
{ "FacePicture", faceBase64 },
|
|
{ "Crop", crop }
|
|
};
|
|
|
|
var signature = ComputeSignature(parameters, "POST", _accessKeySecret);
|
|
var url = $"https://{Endpoint}/?Signature={PercentEncode(signature)}";
|
|
var bodyString = BuildBodyString(parameters);
|
|
|
|
using var fallbackClient = _httpClientFactory == null ? new HttpClient() : null;
|
|
var client = _httpClientFactory?.CreateClient() ?? fallbackClient;
|
|
client.DefaultRequestHeaders.Add("Host", Endpoint);
|
|
var content = new StringContent(bodyString, Encoding.UTF8, "application/x-www-form-urlencoded");
|
|
var response = await client.PostAsync(url, content);
|
|
var rawResponse = await response.Content.ReadAsStringAsync();
|
|
|
|
return ParseResponse(rawResponse);
|
|
}
|
|
|
|
private static AliyunId3MetaVerifyResponse ParseResponse(string rawResponse)
|
|
{
|
|
var result = new AliyunId3MetaVerifyResponse
|
|
{
|
|
RawResponse = rawResponse
|
|
};
|
|
|
|
if (string.IsNullOrWhiteSpace(rawResponse))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
try
|
|
{
|
|
var response = JsonConvert.DeserializeObject<AliyunId3MetaVerifyApiResponse>(rawResponse);
|
|
result.RequestId = response?.RequestId;
|
|
result.Code = response?.Code;
|
|
result.Message = response?.Message;
|
|
result.BizCode = response?.ResultObject?.BizCode;
|
|
result.Similarity = ParseVerifyScore(response?.ResultObject?.FaceDetail);
|
|
result.Passed = response?.ResultObject?.BizCode switch
|
|
{
|
|
"1" => true,
|
|
"2" or "3" => false,
|
|
_ => null
|
|
};
|
|
}
|
|
catch
|
|
{
|
|
result.Message = "阿里云响应不是合法JSON";
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static double? ParseVerifyScore(string faceDetail)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(faceDetail))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var detail = JsonConvert.DeserializeObject<AliyunId3MetaVerifyFaceDetail>(faceDetail);
|
|
return detail?.VerifyScore;
|
|
}
|
|
|
|
private static string ComputeSignature(SortedDictionary<string, string> parameters, string httpMethod, string accessKeySecret)
|
|
{
|
|
var canonicalized = new StringBuilder();
|
|
foreach (var kv in parameters)
|
|
{
|
|
if (kv.Key == "Signature")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (canonicalized.Length > 0)
|
|
{
|
|
canonicalized.Append('&');
|
|
}
|
|
|
|
canonicalized.Append(PercentEncode(kv.Key))
|
|
.Append('=')
|
|
.Append(PercentEncode(kv.Value));
|
|
}
|
|
|
|
var stringToSign = httpMethod + "&" + PercentEncode("/") + "&" + PercentEncode(canonicalized.ToString());
|
|
var signKey = accessKeySecret + "&";
|
|
using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(signKey));
|
|
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
|
|
return Convert.ToBase64String(hash);
|
|
}
|
|
|
|
private static string BuildBodyString(SortedDictionary<string, string> parameters)
|
|
{
|
|
return string.Join("&", parameters
|
|
.Where(x => x.Key != "Signature")
|
|
.Select(x => PercentEncode(x.Key) + "=" + PercentEncode(x.Value)));
|
|
}
|
|
|
|
private static string PercentEncode(string value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
return Uri.EscapeDataString(value)
|
|
.Replace("+", "%20")
|
|
.Replace("*", "%2A")
|
|
.Replace("%7E", "~");
|
|
}
|
|
|
|
private class AliyunId3MetaVerifyApiResponse
|
|
{
|
|
public string RequestId { get; set; }
|
|
|
|
public string Message { get; set; }
|
|
|
|
public string Code { get; set; }
|
|
|
|
public AliyunId3MetaVerifyResultObject ResultObject { get; set; }
|
|
}
|
|
|
|
private class AliyunId3MetaVerifyResultObject
|
|
{
|
|
public string BizCode { get; set; }
|
|
|
|
public string FaceDetail { get; set; }
|
|
}
|
|
|
|
private class AliyunId3MetaVerifyFaceDetail
|
|
{
|
|
[JsonProperty("verifyScore")]
|
|
public double? VerifyScore { get; set; }
|
|
}
|
|
}
|