using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; using System.Text.Json; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; namespace IM.InitCommon.Management; public sealed class InternalClient(HttpClient http, IConfiguration config) { public async Task Send(string service, string path, object? body = null, CancellationToken ct = default) { var address = config[$"Management:Services:{service}"] ?? throw new InvalidOperationException($"未配置服务 {service}"); var key = config["Management:InternalKey"] ?? config["InternalApiKey"]; if (string.IsNullOrWhiteSpace(key)) throw new InvalidOperationException("未配置内部服务密钥"); using var request = new HttpRequestMessage(body is null ? HttpMethod.Get : HttpMethod.Post, new Uri(new Uri(address.TrimEnd('/') + "/"), path.TrimStart('/'))); request.Headers.Add("X-IM-Management-Key", key); if (body is not null) request.Content = JsonContent.Create(body); using var response = await http.SendAsync(request, ct); if (!response.IsSuccessStatusCode) throw new InternalServiceException(service, (int)response.StatusCode); return await response.Content.ReadFromJsonAsync(cancellationToken: ct) ?? throw new InvalidOperationException("服务响应为空"); } public static bool Authorized(HttpContext context) { var c = context.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration; var expected = c?["Management:InternalKey"] ?? c?["InternalApiKey"]; var actual = context.Request.Headers["X-IM-Management-Key"].ToString(); return !string.IsNullOrWhiteSpace(expected) && CryptographicOperations.FixedTimeEquals(SHA256.HashData(Encoding.UTF8.GetBytes(expected)), SHA256.HashData(Encoding.UTF8.GetBytes(actual))); } } public sealed class InternalServiceException(string service, int status) : Exception($"{service} 服务请求失败({status})") { public int Status { get; } = status; }