为保护接口安全性,过滤非法请求来源,本篇博客介绍如何在 ASP.NET Core WebApi 中使用 ActionFilterAttribute 过滤器过滤非法请求。
基本思路:前端在请求头中加入加密后的 Token 和 TimeSpan 两个字段,Token前后端保持一致,加密方法、密钥、向量前后端保持一致,后端在接收到加密的两个字段后解密进行验证,如果来源合法,则返回接口数据,否则给出警告提示。
一、配置 appsettings.json 文件
"BaseInfo": { "WeXinToken": "dieidkdlalsd", "AesKey": "j67aso1dfle3ja45", "AesIv": "2jas5odf7lej6aop" }
二、新建 MyApiFilter 类,重写 OnActionExecuting 方法,过滤非法来源
public class MyApiFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext context) { try { //获取请求头中的Token和TimeSpan var token = context.HttpContext.Request.Headers["Token"].ToString(); var timespan = context.HttpContext.Request.Headers["TimeSpan"].ToString(); //验证timespan和token if (CheckFromIsRight(timespan, token)) { base.OnActionExecuting(context); } else { context.Result = new JsonResult("请求来源非法"); } } catch (Exception) { context.Result = new JsonResult("请求来源非法"); } } public static bool CheckFromIsRight(string timespan, string token) { bool result = false; if (!string.IsNullOrEmpty(timespan) && !string.IsNullOrEmpty(token)) { try { //时间差小于两分钟 if (ConvertToTimeFromSpan(MySecret.AesDecrypt(timespan)).Subtract(DateTime.Now).Minutes <= 2) { //读取配置文件中的token string weixinToken = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", true, true) .Build().GetSection("BaseInfo:WeXinToken").Value; if (MySecret.AesDecrypt(token).Equals(weixinToken)) { return true; } } } catch (Exception) { throw; } } return result; } //将时间戳转换成正常时间 public static DateTime ConvertToTimeFromSpan(string timespan) { //开始时间 TimeZoneInfo.ConvertTimeFromUtc将协调世界时(UTC)转换为本地时区中的时间 DateTime start = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Local); //13位需要加上4个0 TimeSpan ts = new TimeSpan(long.Parse(timespan + "0000")); return start.Add(ts); } }
三、新建 MySecret 类,用于加密解密
public class MySecret { //密钥与向量,与前端保持一致 private static readonly string aeskey = "j67aso1dfle3ja45"; private static readonly string aesiv = "2jas5odf7lej6aop"; /// <summary> /// AES 加密 /// </summary> /// <param name="str">明文(待加密)</param> /// <returns></returns> public static string AesEncrypt(string str) { if (string.IsNullOrEmpty(str)) { return ""; } Byte[] toEncryptArray = Encoding.UTF8.GetBytes(str); RijndaelManaged rm = new RijndaelManaged { Key = Encoding.UTF8.GetBytes(aeskey), Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7, IV = Encoding.UTF8.GetBytes(aesiv), }; ICryptoTransform cTransform = rm.CreateEncryptor(); Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); return Convert.ToBase64String(resultArray, 0, resultArray.Length); } /// <summary> /// AES 解密 /// </summary> /// <param name="str">明文(待解密)</param> /// <returns></returns> public static string AesDecrypt(string str) { if (string.IsNullOrEmpty(str)) { return ""; } try { Byte[] toEncryptArray = Convert.FromBase64String(str); RijndaelManaged rm = new RijndaelManaged { Key = Encoding.UTF8.GetBytes(aeskey), Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7, IV = Encoding.UTF8.GetBytes(aesiv), }; ICryptoTransform cTransform = rm.CreateDecryptor(); Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); return Encoding.UTF8.GetString(resultArray); } catch (Exception) { return str; } } }
tips:为提高安全性,密钥与向量应配置在appsetting.json文件中再封装方法获取,本篇博客为简化代码未配置。
微信小程序crypto.js AES加解密:https://www.cnblogs.com/gygg/p/12793227.html
四、使用过滤器
在需要过滤的 Controller 中加入【MyApiFilter】特性
在请求头中加入timespan和token字段
请求来源合法,返回接口数据
否则返回警告提示
End!