Files
simapi-net/SwaggerFilters/SimApiSignOperationFilter.cs
T
2025-12-18 09:08:35 +08:00

101 lines
3.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi;
using SimApi.Attributes;
using SimApi.ModelBinders;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace SimApi.SwaggerFilters;
public class SimApiSignOperationFilter(IServiceProvider serviceProvider) : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
// 1. 检查当前方法或类是否标注了 SimApiSignAttribute 及其子类
var signAttribute = context.MethodInfo.GetCustomAttribute<SimApiSignAttribute>(inherit: true)
?? context.MethodInfo.DeclaringType?.GetCustomAttribute<SimApiSignAttribute>(inherit: true);
if (signAttribute == null)
{
return;
}
var keyProviderType = signAttribute.KeyProvider;
if (!typeof(SimApiSignProviderBase).IsAssignableFrom(keyProviderType))
{
throw new InvalidOperationException($"KeyProvider 必须继承自 {nameof(SimApiSignProviderBase)}");
}
SimApiSignProviderBase? keyProvider;
try
{
keyProvider =
serviceProvider.CreateScope().ServiceProvider.GetService(keyProviderType) as SimApiSignProviderBase;
}
catch (Exception ex)
{
throw new InvalidOperationException($"无法从 DI 容器获取 {keyProviderType.Name} 实例:{ex.Message}");
}
if (keyProvider == null)
{
throw new InvalidOperationException($"{keyProviderType.Name} 未在 DI 容器中注册");
}
var signStr = keyProvider.SignFields.Aggregate(string.Empty, (current, field) => current + $"{field}=xxx&");
if (!string.IsNullOrEmpty(keyProvider.AppIdName))
{
signStr += $"{keyProvider.AppIdName}=xxx&";
}
signStr += $"{keyProvider.TimestampName}=xxx&{keyProvider.NonceName}=xxx&签名密钥";
var signParameters = new List<(string Name, string Description, bool Required)>
{
(keyProvider.TimestampName, "时间戳(秒级)", true),
(keyProvider.NonceName, "随机字符串", true),
(keyProvider.SignName, $"MD5签名结果,签名MD5字符串: {signStr}", true)
};
if (keyProvider.AppIdName != null)
{
signParameters.Add((keyProvider.AppIdName, "应用标识", true));
}
signParameters.AddRange(keyProvider.SignFields.Where(x => x != keyProvider.AppIdName)
.Select(f => (f, string.Empty, true)));
operation.Parameters ??= new List<IOpenApiParameter>();
foreach (var (name, description, required) in signParameters)
{
if (operation.Parameters?.Any(p => p.Name == name) == true)
{
var tmp = operation.Parameters?.FirstOrDefault(p => p.Name == name);
if (tmp is OpenApiParameter concreteParam)
{
// 重新赋值只读属性(通过实例化新对象覆盖,或直接修改具体类的可写属性)
concreteParam.Required = required; // OpenApiParameter 的 Required 有 setter
concreteParam.Description = description;
}
continue;
}
operation.Parameters ??= new List<IOpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter
{
Name = name,
In = ParameterLocation.Query, // 指定为 Query 参数
Description = description,
Required = required,
Schema = new OpenApiSchema
{
Type = JsonSchemaType.String // 签名相关参数通常为字符串类型
}
});
}
}
}