using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace XiangYi.Infrastructure.RealName;
///
/// 阿里云实名认证服务实现
/// 使用阿里云身份证二要素核验API(纯服务端接入)
///
public class AliyunRealNameProvider : IRealNameProvider
{
private readonly AliyunRealNameOptions _options;
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
// 身份证二要素核验API
private const string IdCardVerifyHost = "cloudauth.aliyuncs.com";
private const string ApiVersion = "2019-03-07";
public AliyunRealNameProvider(
IOptions options,
IHttpClientFactory httpClientFactory,
ILogger logger)
{
_options = options.Value.Aliyun ?? new AliyunRealNameOptions();
_logger = logger;
_httpClient = httpClientFactory.CreateClient("AliyunRealName");
}
///
/// 身份证二要素验证(姓名+身份证号)
///
public async Task VerifyIdCardAsync(string name, string idCard)
{
try
{
// 验证配置
if (string.IsNullOrEmpty(_options.AccessKeyId))
{
_logger.LogError("阿里云实名认证配置错误: AccessKeyId 为空");
return RealNameResult.Fail("CONFIG_ERROR", "实名认证服务配置错误,请联系管理员");
}
if (string.IsNullOrEmpty(_options.AccessKeySecret))
{
_logger.LogError("阿里云实名认证配置错误: AccessKeySecret 为空");
return RealNameResult.Fail("CONFIG_ERROR", "实名认证服务配置错误,请联系管理员");
}
_logger.LogInformation("阿里云实名认证配置: AccessKeyId={AccessKeyId}, SecretLength={SecretLength}",
_options.AccessKeyId[..8] + "***",
_options.AccessKeySecret?.Length ?? 0);
var parameters = new SortedDictionary
{
{ "Action", "Id2MetaVerify" },
{ "Version", ApiVersion },
{ "Format", "JSON" },
{ "AccessKeyId", _options.AccessKeyId },
{ "SignatureMethod", "HMAC-SHA1" },
{ "Timestamp", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ") },
{ "SignatureVersion", "1.0" },
{ "SignatureNonce", Guid.NewGuid().ToString("N") },
{ "ParamType", "normal" },
{ "IdentifyNum", idCard },
{ "UserName", name }
};
var signature = GenerateSignature(parameters);
parameters.Add("Signature", signature);
var queryString = string.Join("&", parameters.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}"));
var requestUrl = $"https://{IdCardVerifyHost}/?{queryString}";
_logger.LogInformation("阿里云实名认证请求: {Url}", requestUrl.Replace(_options.AccessKeyId, "***"));
var response = await _httpClient.GetAsync(requestUrl);
var content = await response.Content.ReadAsStringAsync();
_logger.LogInformation("阿里云实名认证响应: {Response}", content);
var result = JsonSerializer.Deserialize(content, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
if (result?.Code == "200" && result.ResultObject?.BizCode == "1")
{
_logger.LogInformation("阿里云身份证二要素验证通过: {IdCard}", MaskIdCard(idCard));
return RealNameResult.Success(result.RequestId);
}
var errorMsg = GetErrorMessage(result?.ResultObject?.BizCode, result?.Message);
_logger.LogWarning("阿里云身份证二要素验证失败: {IdCard}, BizCode: {BizCode}, Error: {Error}",
MaskIdCard(idCard), result?.ResultObject?.BizCode, errorMsg);
return RealNameResult.Fail(
result?.ResultObject?.BizCode ?? result?.Code ?? "UNKNOWN",
errorMsg,
result?.RequestId);
}
catch (Exception ex)
{
_logger.LogError(ex, "阿里云身份证二要素验证异常: {IdCard}", MaskIdCard(idCard));
return RealNameResult.Fail("EXCEPTION", ex.Message);
}
}
///
/// 身份证三要素验证(阿里云纯API模式不支持,返回失败)
///
public Task VerifyWithPhotoAsync(string name, string idCard, string photoBase64)
{
_logger.LogWarning("阿里云纯API模式不支持身份证三要素验证(人脸比对)");
return Task.FromResult(RealNameResult.Fail("NOT_SUPPORTED", "当前配置不支持人脸比对验证,请使用二要素验证"));
}
///
/// 身份证OCR识别(正面)- 阿里云纯API模式不支持
///
public Task OcrIdCardFrontAsync(string imageBase64)
{
_logger.LogWarning("阿里云纯API模式不支持身份证OCR识别");
return Task.FromResult(IdCardOcrResult.Fail("NOT_SUPPORTED", "当前配置不支持身份证OCR识别,请手动输入身份信息"));
}
///
/// 身份证OCR识别(反面)- 阿里云纯API模式不支持
///
public Task OcrIdCardBackAsync(string imageBase64)
{
_logger.LogWarning("阿里云纯API模式不支持身份证OCR识别");
return Task.FromResult(IdCardOcrResult.Fail("NOT_SUPPORTED", "当前配置不支持身份证OCR识别,请手动输入身份信息"));
}
///
/// 生成阿里云API签名
///
private string GenerateSignature(SortedDictionary parameters)
{
var canonicalizedQueryString = string.Join("&",
parameters.Select(p => $"{PercentEncode(p.Key)}={PercentEncode(p.Value)}"));
var stringToSign = $"GET&%2F&{PercentEncode(canonicalizedQueryString)}";
// 确保 AccessKeySecret 不为空
var secretKey = _options.AccessKeySecret ?? string.Empty;
using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(secretKey + "&"));
var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
return Convert.ToBase64String(hashBytes);
}
///
/// URL编码(阿里云特殊规则)
///
private static string PercentEncode(string value)
{
if (string.IsNullOrEmpty(value))
return string.Empty;
var encoded = Uri.EscapeDataString(value);
// 阿里云特殊编码规则
encoded = encoded.Replace("+", "%20")
.Replace("*", "%2A")
.Replace("%7E", "~");
return encoded;
}
///
/// 获取错误信息
///
private static string GetErrorMessage(string? bizCode, string? message)
{
return bizCode switch
{
"1" => "验证通过",
"2" => "姓名与身份证号不一致",
"3" => "身份证号不存在",
"4" => "身份证号格式错误",
"5" => "服务异常,请稍后重试",
_ => message ?? "验证失败,请检查身份信息是否正确"
};
}
private static string MaskIdCard(string idCard)
{
if (string.IsNullOrEmpty(idCard) || idCard.Length < 10)
return "***";
return $"{idCard[..4]}**********{idCard[^4..]}";
}
#region 响应模型
private class AliyunIdVerifyResponse
{
public string? Code { get; set; }
public string? Message { get; set; }
public string? RequestId { get; set; }
public AliyunIdVerifyResultObject? ResultObject { get; set; }
}
private class AliyunIdVerifyResultObject
{
public string? BizCode { get; set; }
}
#endregion
}