為保護接口安全性,過濾非法請求來源,本篇博客介紹如何在 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!
