|
|
|
|
@ -4,6 +4,7 @@ using Common.Shared.Application.DaHua; |
|
|
|
|
using Common.Shared.DomainService; |
|
|
|
|
using Microsoft.Extensions.Configuration; |
|
|
|
|
using Microsoft.Extensions.Logging; |
|
|
|
|
using MongoDB.Bson; |
|
|
|
|
using Org.BouncyCastle.Crypto.Parameters; |
|
|
|
|
using Org.BouncyCastle.Security; |
|
|
|
|
using System; |
|
|
|
|
@ -18,9 +19,10 @@ namespace Alarm.DomainService.DahAlarm |
|
|
|
|
{ |
|
|
|
|
private readonly ILogger<DahuaGeneralCtlService> _logger; |
|
|
|
|
private readonly IConfiguration _configuration; |
|
|
|
|
private readonly HttpClient _http; |
|
|
|
|
|
|
|
|
|
private readonly MQTTClient _mqttClient; |
|
|
|
|
private readonly IMqttClientService _mqttClientService; |
|
|
|
|
private readonly ITokenProviderService _tokenProviderService; |
|
|
|
|
private string mqttHostIp; |
|
|
|
|
private int mqttHostPort; |
|
|
|
|
private int mqttTimeout; |
|
|
|
|
@ -36,11 +38,11 @@ namespace Alarm.DomainService.DahAlarm |
|
|
|
|
/// <param name="configuration"></param> |
|
|
|
|
/// <param name="http"></param> |
|
|
|
|
/// <param name="mQTTClient"></param> |
|
|
|
|
public DahuaGeneralCtlService(ILogger<DahuaGeneralCtlService> logger, IConfiguration configuration, HttpClient http, MQTTClient mQTTClient, IMqttClientService mqttClientService) |
|
|
|
|
public DahuaGeneralCtlService(ILogger<DahuaGeneralCtlService> logger, IConfiguration configuration, MQTTClient mQTTClient, IMqttClientService mqttClientService, ITokenProviderService tokenProviderService) |
|
|
|
|
{ |
|
|
|
|
_logger = logger; |
|
|
|
|
_configuration = configuration; |
|
|
|
|
_http = http; |
|
|
|
|
|
|
|
|
|
_mqttClient = mQTTClient; |
|
|
|
|
mqttHostIp = _configuration["SubscribeMQTT:HostIP"]!;//IP地址 |
|
|
|
|
mqttHostPort = _configuration["SubscribeMQTT:HostPort"].ToInt();//端口号 |
|
|
|
|
@ -50,126 +52,30 @@ namespace Alarm.DomainService.DahAlarm |
|
|
|
|
mqttClientId = _configuration["SubscribeMQTT:ClientId"]!; |
|
|
|
|
topicName = _configuration["SubscribeMQTT:TopicName"]!; |
|
|
|
|
_mqttClientService = mqttClientService; |
|
|
|
|
_tokenProviderService = tokenProviderService; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 获取公钥 |
|
|
|
|
/// 开发测试的时候,忽略证书 |
|
|
|
|
/// </summary> |
|
|
|
|
/// <returns></returns> |
|
|
|
|
/// <exception cref="NotImplementedException"></exception> |
|
|
|
|
public async Task<DaHApiResult<PublicKeyDto>> GetPublicKey() |
|
|
|
|
{ |
|
|
|
|
DaHApiResult<PublicKeyDto> result = new() { Success = true, Code = "0", Data = new PublicKeyDto() { PublicKey = "" } }; |
|
|
|
|
try |
|
|
|
|
private static readonly HttpClient _http = new HttpClient(new HttpClientHandler |
|
|
|
|
{ |
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/public-key"; |
|
|
|
|
|
|
|
|
|
using var resp = await _http.GetAsync(url); |
|
|
|
|
resp.EnsureSuccessStatusCode(); |
|
|
|
|
var json = await resp.Content.ReadAsStringAsync(); |
|
|
|
|
var envelope = JsonSerializer.Deserialize<DaHApiResult<PublicKeyDto>>(json, new JsonSerializerOptions |
|
|
|
|
{ |
|
|
|
|
PropertyNameCaseInsensitive = true |
|
|
|
|
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
if (envelope?.Data?.PublicKey is null or { Length: 0 }) |
|
|
|
|
{ |
|
|
|
|
_logger.LogWarning("获取大华公钥失败,返回结果:{Result}", json); |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1001"; |
|
|
|
|
result.Msg = "获取大华公钥失败"; |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
result.Data.PublicKey = envelope.Data.PublicKey; |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
_logger.LogWarning(ex, "大华平台获取公钥出错"); |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 获取token |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="dto"></param> |
|
|
|
|
/// <returns></returns> |
|
|
|
|
public async Task<DaHApiResult<LoginResDto>> GetToken(LoginRequestDto dto) |
|
|
|
|
{ |
|
|
|
|
DaHApiResult<LoginResDto> result = new DaHApiResult<LoginResDto>() { Success = true, Code = "0" }; |
|
|
|
|
if (dto is null) |
|
|
|
|
{ |
|
|
|
|
result.Success = false; |
|
|
|
|
|
|
|
|
|
result.Code = "1002"; |
|
|
|
|
result.Msg = "请求参数不能为空"; |
|
|
|
|
_logger.LogWarning("获取大华登录令牌失败,参数不能为空"); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
if (string.IsNullOrWhiteSpace(dto.Password)) |
|
|
|
|
{ |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1003"; |
|
|
|
|
result.Msg = "密码不能为空"; |
|
|
|
|
_logger.LogWarning("获取大华登录令牌失败,密码不能为空"); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
//1. 获取公钥 |
|
|
|
|
DaHApiResult<PublicKeyDto> publicKeyResult = await GetPublicKey(); |
|
|
|
|
if (!publicKeyResult.Success) |
|
|
|
|
{ |
|
|
|
|
result.Code = "500"; |
|
|
|
|
result.Msg = publicKeyResult.Msg; |
|
|
|
|
_logger.LogWarning("获取大华公钥失败:{Msg}", publicKeyResult.Msg); |
|
|
|
|
} |
|
|
|
|
//2. 鉴权 |
|
|
|
|
dto.PublicKey = publicKeyResult.Data.PublicKey; |
|
|
|
|
|
|
|
|
|
var url = $"https://{_configuration["DahuaAuth:Host"]}/evo-apigw/evo-oauth/1.0.0/oauth/extend/token"; |
|
|
|
|
//必须加密 |
|
|
|
|
dto.Password = EncryptByPublicKey(dto.Password, dto.PublicKey!); |
|
|
|
|
using var resp = await _http.PostAsJsonAsync(url, dto); |
|
|
|
|
resp.EnsureSuccessStatusCode(); |
|
|
|
|
|
|
|
|
|
var tokenInfo = await resp.Content.ReadFromJsonAsync<DaHApiResult<LoginResDto>>(); |
|
|
|
|
|
|
|
|
|
if (tokenInfo == null || !result.Success || result.Code != "0") |
|
|
|
|
{ |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1004"; |
|
|
|
|
result.Msg = "获取大华登录令牌失败"; |
|
|
|
|
_logger.LogWarning("获取大华登录令牌失败,返回结果:{Result}", result); |
|
|
|
|
} |
|
|
|
|
//固定的拼接方式 |
|
|
|
|
result.Data.AccessToken = string.Concat(tokenInfo!.Data.TokenType, " ", tokenInfo.Data.AccessToken); |
|
|
|
|
} |
|
|
|
|
catch (Exception ex) |
|
|
|
|
{ |
|
|
|
|
_logger.LogError(ex, "获取大华登录令牌出错"); |
|
|
|
|
result.Success = false; |
|
|
|
|
result.Code = "1004"; |
|
|
|
|
result.Msg = "获取大华登录令牌失败"; |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// 新增报警事件订阅 |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="dto"></param> |
|
|
|
|
/// <returns></returns> |
|
|
|
|
/// <exception cref="NotImplementedException"></exception> |
|
|
|
|
public async Task<DaHApiResult<object>> AddSubscribeEvent(string accessToken) |
|
|
|
|
public async Task<DaHApiResult<object>> AddSubscribeEvent() |
|
|
|
|
{ |
|
|
|
|
var result = new DaHApiResult<object> { Success = true, Code = "0", Data = new object() }; |
|
|
|
|
|
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
var baseHost = _configuration["DahuaEvent:Host"]; |
|
|
|
|
var baseHost = _configuration["DahuaAuth:Host"]; |
|
|
|
|
if (string.IsNullOrWhiteSpace(baseHost)) |
|
|
|
|
{ |
|
|
|
|
return new DaHApiResult<object> |
|
|
|
|
@ -186,28 +92,41 @@ namespace Alarm.DomainService.DahAlarm |
|
|
|
|
{ |
|
|
|
|
Param = new SubscribeParam |
|
|
|
|
{ |
|
|
|
|
Monitors = new List<MonitorEntry> |
|
|
|
|
Monitors = new List<MonitorConfig> |
|
|
|
|
{ |
|
|
|
|
new() |
|
|
|
|
new MonitorConfig |
|
|
|
|
{ |
|
|
|
|
Monitor = _configuration["DahuaEvent:Callback"]!, |
|
|
|
|
Events = |
|
|
|
|
[ |
|
|
|
|
new() |
|
|
|
|
MonitorUrl = _configuration["DahuaAuth:Callback"], |
|
|
|
|
MonitorType = "url", |
|
|
|
|
Events = new List<EventConfig> |
|
|
|
|
{ |
|
|
|
|
new EventConfig |
|
|
|
|
{ |
|
|
|
|
Category = "alarm", |
|
|
|
|
SubscribeAll = 1, |
|
|
|
|
DomainSubscribe = 2, |
|
|
|
|
|
|
|
|
|
EventType = 1 |
|
|
|
|
Authorities = new List<Authority> |
|
|
|
|
{ |
|
|
|
|
new Authority |
|
|
|
|
{ |
|
|
|
|
Types = new List<string> { "4321" } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
] |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
Subsystem = new SubsystemInfo { SubsystemType = 0, Name = _configuration["DahuaEvent:SubsystemName"]!, Magic = _configuration["DahuaEvent:SubsystemMagic"]! } |
|
|
|
|
Subsystem = new SubsystemConfig |
|
|
|
|
{ |
|
|
|
|
SubsystemType = 0, |
|
|
|
|
Name = "192.168.21.43_alarm", |
|
|
|
|
Magic = "192.168.21.43" |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
var clientId = _configuration["DahuaAuth:ClientId"]; |
|
|
|
|
var token = await _tokenProviderService.GetTokenAsync(clientId!); |
|
|
|
|
// —— 发起请求 —— |
|
|
|
|
using var request = new HttpRequestMessage(HttpMethod.Post, url) |
|
|
|
|
{ |
|
|
|
|
@ -219,7 +138,7 @@ namespace Alarm.DomainService.DahAlarm |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// 大华网关要求:Authorization 放完整值(Bearer xxx) |
|
|
|
|
request.Headers.TryAddWithoutValidation("Authorization", accessToken); |
|
|
|
|
request.Headers.TryAddWithoutValidation("Authorization", token); |
|
|
|
|
request.Headers.Accept.ParseAdd("application/json"); |
|
|
|
|
|
|
|
|
|
using var resp = await _http.SendAsync(request); |
|
|
|
|
@ -303,61 +222,5 @@ namespace Alarm.DomainService.DahAlarm |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#region RES加密 |
|
|
|
|
|
|
|
|
|
private static String EncryptByPublicKey(String context, String publicKey) |
|
|
|
|
{ |
|
|
|
|
RSACryptoServiceProvider rsa = new(); |
|
|
|
|
|
|
|
|
|
rsa.ImportParameters(FromXmlStringExtensions(ConvertToXmlPublicJavaKey(publicKey))); |
|
|
|
|
byte[] byteText = System.Text.Encoding.UTF8.GetBytes(context); |
|
|
|
|
byte[] byteEntry = rsa.Encrypt(byteText, false); |
|
|
|
|
return Convert.ToBase64String(byteEntry); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static RSAParameters FromXmlStringExtensions(string xmlString) |
|
|
|
|
{ |
|
|
|
|
RSAParameters parameters = new RSAParameters(); |
|
|
|
|
|
|
|
|
|
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument(); |
|
|
|
|
xmlDoc.LoadXml(xmlString); |
|
|
|
|
|
|
|
|
|
if (xmlDoc.DocumentElement!.Name.Equals("RSAKeyValue")) |
|
|
|
|
{ |
|
|
|
|
foreach (System.Xml.XmlNode node in xmlDoc.DocumentElement.ChildNodes) |
|
|
|
|
{ |
|
|
|
|
switch (node.Name) |
|
|
|
|
{ |
|
|
|
|
case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
throw new Exception("Invalid XML RSA key."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return parameters; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static string ConvertToXmlPublicJavaKey(string publicJavaKey) |
|
|
|
|
{ |
|
|
|
|
RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicJavaKey)); |
|
|
|
|
string xmlpublicKey = string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>", |
|
|
|
|
Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()), |
|
|
|
|
Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned())); |
|
|
|
|
Console.WriteLine(xmlpublicKey); |
|
|
|
|
return xmlpublicKey; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endregion RES加密 |
|
|
|
|
} |
|
|
|
|
} |