- 利用ActionFilterAttribute解析外部接受数据,并验签
/// <summary>
/// 外部api请求时,进行签名校验
/// </summary>
public class ExterApiFilter : ActionFilterAttribute
{
string token = "abc@#";
/// <summary>
/// 进入系统,对外部请求的进行验签操作
/// 1、除签名外字段按ASCII码从小到大排序
/// 2、最后连接私有的token
/// 3、按key=value形式拼接
/// 4、签名校验
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
try
{
//入参解析
var paras = filterContext.ActionParameters;
var i = filterContext.ActionDescriptor.GetParameters();
Dictionary<string, object> inParamDics = new Dictionary<string, object>();//入参集合
foreach (var item in i)
{
var itemType = item.ParameterType;
if (itemType.IsClass && !itemType.Name.Equals("String", System.StringComparison.OrdinalIgnoreCase))//类
{
GetPropertyDic(inParamDics, itemType.GetProperties(), filterContext.ActionParameters[item.ParameterName]);
}
else
{
if (!inParamDics.ContainsKey(item.ParameterName)) inParamDics.Add(item.ParameterName, filterContext.Controller.ValueProvider.GetValue(item.ParameterName).AttemptedValue);
}
}
Dictionary<string, string> inParam = new Dictionary<string, string>();
string sign = string.Empty;
#region 解析数据
if (inParamDics.Keys.Contains("sign"))
{
sign = inParamDics["sign"]?.ToString();
}
inParam = inParamDics.Where(x => !string.IsNullOrEmpty(x.Value?.ToString())).ToDictionary(k => k.Key, k => k.Value?.ToString());
if (inParam.ContainsKey("sign")) inParam.Remove("sign");
var signScret = new Dictionary<string, string>() { ["token"] = token };
#endregion 解析数据
bool isCkSign = !string.IsNullOrEmpty(sign) && KosSigns.GetMd5Sign(inParam, signScret).Equals(sign, System.StringComparison.OrdinalIgnoreCase);
if (!isCkSign)
{
filterContext.Result = new JsonResult() { Data = new { Result = false, Message = "验签失败" } };
}
}
catch (Exception ex)
{
filterContext.Result = new JsonResult() { Data = new { Result = false, Message = "内部错误" } };
}
base.OnActionExecuting(filterContext);
}
/// <summary>
/// 实体类解析成键值对
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="inParam">入参</param>
/// <param name="infos">实体的属性集合</param>
/// <param name="entity">实体</param>
private void GetPropertyDic<T>(Dictionary<string, object> inParam, PropertyInfo[] infos, T entity)
{
foreach (var info in infos)
{
var itemTpe = info.PropertyType;
if (itemTpe.IsClass && !itemTpe.Name.Equals("String", StringComparison.OrdinalIgnoreCase))
{
if (info.GetValue(entity) == null) continue;
GetPropertyDic(inParam, itemTpe.GetProperties(), info.GetValue(entity));
}
else
{
inParam.Add(info.Name, info.GetValue(entity, null));
}
}
}
}
2.Controller中的action标记,验签通过后的方法请求。供外部请求http://localhost:80/ApiFilter/GetApiFilter
[ExterApiFilter]
public JsonResult GetApiFilter(GetApiFilterInput input)
{
bool resultStatus = false;
//入参判断
if (string.IsNullOrEmpty(input.Name)) return Json(new { Result = false, Message = "名称不能为空" });
//查询数据
var outputs = _iService.GetApiFilter(input);
resultStatus = outputs != null && outputs.TotalCount > 0;
if (!resultStatus) Json(new { Result = resultStatus, Message = "无相关数据" });
return Json(new { Result = resultStatus, Data = outputs });
}
3. 私有方法之:类实体的属性与值转化成键值对形式
/// <summary>
/// 类实体的属性与值转化成键值对形式
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="inParam"></param>
/// <param name="infos"></param>
/// <param name="Entity"></param>
private static void GetEntityPropertyToDic<T>(Dictionary<string, object> inParam, PropertyInfo[] infos, T Entity)
{
foreach (var info in infos)
{
var itemTpe = info.PropertyType;
//实体中有嵌套类的处理
if (itemTpe.IsClass && !itemTpe.Name.Equals("String", StringComparison.OrdinalIgnoreCase))
{
if (info.GetValue(Entity) == null) continue;
GetEntityPropertyToDic(inParam, itemTpe.GetProperties(), info.GetValue(Entity));
}
else
{
inParam.Add(info.Name, info.GetValue(Entity, null));
}
}
}
4.私有方法之:Md5加密,并且参数ASCII码排序
/// <summary>
/// Md5加密
/// </summary>
/// <param name="param"></param>
/// <param name="signScret"></param>
/// <returns></returns>
private static string GetMd5Sign(Dictionary<string, string> param, Dictionary<string, string> signScret)
{
var sign_s = ComSecurity.GetASCIIParamMap(param);
if (signScret != null && signScret.Any())
{
foreach (var item in signScret)
{
sign_s = sign_s + $"&{item.Key}=" + item.Value;
}
}
return ComSecurity.Md5Encrypt(sign_s);
}
5.私有方法之:http的post请求
private static string GetPostUrl(string url, string postData, Dictionary<string, string> heardsDic = null)
{
string result = string.Empty;
try
{
HttpWebRequest request = null;
if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
request = WebRequest.Create(url) as HttpWebRequest;
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
request.ProtocolVersion = HttpVersion.Version11;
// 这里设置了协议类型。
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;// SecurityProtocolType.Tls1.2;
request.KeepAlive = false;
ServicePointManager.CheckCertificateRevocationList = true;
ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.Expect100Continue = false;
}
else
{
request = (HttpWebRequest)WebRequest.Create(url);
}
request.Method = "POST"; //使用get方式发送数据
request.ContentType = "application/json";
request.Referer = null;
request.AllowAutoRedirect = true;
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";
request.Accept = "*/*";
#region 请求头数据
if (heardsDic != null)
{
foreach (var item in heardsDic)
{
SetHeaderValue(request.Headers, item.Key, item.Value);
}
}
#endregion 请求头数据
byte[] data = Encoding.UTF8.GetBytes(postData);
Stream newStream = request.GetRequestStream();
newStream.Write(data, 0, data.Length);
newStream.Close();
//获取网页响应结果
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream stream = response.GetResponseStream();
using (StreamReader sr = new StreamReader(stream))
{
result = sr.ReadToEnd();
}
}
catch (Exception ex)
{
}
return result;
}
private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true; //总是接受
}
private static void SetHeaderValue(WebHeaderCollection header, string name, string value)
{
var property = typeof(WebHeaderCollection).GetProperty("InnerCollection", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (property != null)
{
var collection = property.GetValue(header, null) as NameValueCollection;
collection[name] = value;
}
}
6.外部生成签名并发起请求
//签名 私钥
string token = "abc@#";
var data= new TestManageDemo() { Id = 10, Age = 20, Name = "少侠", Address = "天涯海角路", Interest = new TestInterest() { InterestName = "仗剑撸代码", InterestAge = 9 } };
//反射
Type type = data.GetType();
System.Reflection.PropertyInfo[] propertyInfos = type.GetProperties();
Dictionary<string, string> inParam = new Dictionary<string, string>();
Dictionary<string, object> inParamDics = new Dictionary<string, object>();
//类实体转键值对
GetEntityPropertyToDic(inParamDics, propertyInfos, data);
inParam = inParamDics.ToDictionary(k => k.Key, k => k.Value?.ToString());
if (inParam.ContainsKey("sign")) inParam.Remove("sign");
//md5签名
data.sign = GetMd5Sign(inParam, new Dictionary<string, string>() { ["token"] = token });
//http请求
st = GetPostUrl("http://localhost:80/ApiFilter/GetApiFilter", JsonConvert.SerializeObject(data));
本文介绍如何在.NET中使用ActionFilterAttribute进行API请求的统一校验,特别是针对外部API的MD5签名验证过程。通过Controller的action标记,确保只有验签通过的请求才能访问指定的方法。详细讲解了将类实体属性转化为键值对、MD5加密及ASCII排序、HTTP POST请求的实现步骤,以及外部如何生成签名并发起请求。
844

被折叠的 条评论
为什么被折叠?



