文章目录
- 为什么是用中间件而不是筛选器?
- 代码实现
- 技术要点
- context.Request.EnableBuffering()
- 指针问题
- 小结
为什么是用中间件而不是筛选器?
为什么要用中间件验签,而不是筛选器去验签?
1、根据上图我们可以看到,中间件在筛选器之前,而筛选往下就是我们写业务逻辑代码的控制器了。这就大大增加了我们被攻击的风险。
2、用筛选器我们需要在每个控制器上都添加相应的标识,如果需要校验的sign的控制器多的话,就增加了很多不必要的工作量,和风险,如果某个控制器一时疏忽忘记加筛选器的话就有可能会被攻击。
筛选器一般都是当数据得到信任的时候做验证,例如用户登录了,做功能的权限判定,中间件判定非信任数据
代码实现
/// <summary>
/// 验签中间件
/// </summary>
public class SignatureMiddleware
{ /// <summary>
/// 用户服务
/// </summary>
public UserService _userService { get; set; }
private readonly RequestDelegate _next;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="next">上下文</param>
/// <param name="userService">用户服务注入</param>
public SignatureMiddleware(RequestDelegate next, UserService userService)
{
_next = next;
_userService = userService;
}
/// <summary>
/// 管道委托
/// </summary>
/// <param name="context">请求</param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.Value.StartsWith("/api/Order"))
{
// 验证签名
var isValidSignature = await ValidateSignatureAsync(context);
if (isValidSignature.Item1)
{
await _next(context);
}
else
{
context.Response.StatusCode = 403;
await context.Response.WriteAsJsonAsync(AlwaysResult.Error(isValidSignature.Item2));
}
}
else
{
await _next(context);
}
}
private async Task<(bool, string)> ValidateSignatureAsync(HttpContext context)
{
context.Request.EnableBuffering();//倒带
string Postbody = string.Empty;
string sign = context.Request.Headers[GlobalContext.SystemConfig.OpenApiSettings.SignName].ParseToString();
string timestamp = context.Request.Headers[GlobalContext.SystemConfig.OpenApiSettings.Timestamp].ParseToString();
string appkey = context.Request.Headers[GlobalContext.SystemConfig.OpenApiSettings.Appkey].ParseToString();
//先根据Appkey查询这个账户的状态:
UserExtend user = await _userService.GetForm(appkey);
if (user != null)
{
context.Request.Body.Position = 0;
var readResult = await context.Request.BodyReader.ReadAsync();
context.Request.BodyReader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
Postbody = Encoding.UTF8.GetString(readResult.Buffer.FirstSpan);
string checksign = DESEncrypt.MD5(appkey + timestamp + Postbody + user.F_Account + appkey).ToLower();
if (!checksign.Equals(sign))
{
return (false, "验签失败!");
}
else
{
context.Request.Body.Position = 0;//指针回拨
return (true, "");
}
}
else
{
return (false, "非法签名!");
}
}
}
技术要点
context.Request.EnableBuffering()
ValidateSignatureAsync 方法为验证签名方法,方法内第一行中的:context.Request.EnableBuffering();的作用是允许http请求中的body重复读取,如果不加这个方法当数据在验签过程中读取出来之后到了控制器时,控制器中获取到的body就会是空值
指针问题
context.Request.Body.Position ;
代码中用到了两次,第一次使用的时候是将指针指向body的第一位。当读取出数据之后,指针会由第一位移动到最后一位,因此我们在读取完验签完成之后需要把指针从最后以为拨回到第一位。如果没有验签通过也就没有那个必要了。
小结
在学习过程中我们不仅要知其然还要知其所以然。
中间件:用于过滤非信任数据
过滤器:用于判定受信任的安全的数据