You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core中能否两次读取Request.Body?签名验证遇模型绑定问题

请求体签名验证过滤器导致模型绑定失败的解决方案

问题原因

你的验证过滤器读取Request.Body后,尝试通过Position = 0重置流位置,但ASP.NET Core默认的HttpRequestStream是不可重读的单向流,不支持查找操作,所以重置位置无效。后续模型绑定读取请求体时,流已处于末尾,无法获取内容,导致MyModel实例属性全部为null。

解决方案

方案1:启用请求体缓冲(推荐)

调用Request.EnableBuffering()方法,让ASP.NET Core将请求体缓存到内存或临时文件中,使流支持重读和位置重置。修改过滤器代码如下:

public class Verify : ActionFilter, IAsyncAuthorizationFilter 
{
  public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
  {
    var request = context.HttpContext.Request;
    request.EnableBuffering(); // 启用缓冲,使流可重读

    var signature = request.Headers["x-signature"].FirstOrDefault();
    if (string.IsNullOrEmpty(signature))
    {
      context.Result = new UnauthorizedResult();
      return;
    }

    var requestBody = await new StreamReader(request.Body).ReadToEndAsync();
    request.Body.Position = 0; // 现在重置位置有效,后续模型绑定可正常读取

    var key = "my-secure-key";
    if (EncryptTheBody(requestBody, key) != signature) 
    {
      context.Result = new UnauthorizedResult();
      return;
    }
  }
}

方案2:替换为内存流

将读取后的请求体内容存入MemoryStream,并替换原来的Request.Body,确保后续读取操作能获取到内容:

public class Verify : ActionFilter, IAsyncAuthorizationFilter 
{
  public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
  {
    var request = context.HttpContext.Request;
    var signature = request.Headers["x-signature"].FirstOrDefault();
    if (string.IsNullOrEmpty(signature))
    {
      context.Result = new UnauthorizedResult();
      return;
    }

    // 将请求体复制到内存流
    using var memoryStream = new MemoryStream();
    await request.Body.CopyToAsync(memoryStream);
    memoryStream.Position = 0;

    // 从内存流读取内容用于验证
    var requestBody = await new StreamReader(memoryStream).ReadToEndAsync();
    memoryStream.Position = 0; // 重置内存流位置

    // 替换原请求体为内存流
    request.Body = memoryStream;

    var key = "my-secure-key";
    if (EncryptTheBody(requestBody, key) != signature) 
    {
      context.Result = new UnauthorizedResult();
      return;
    }
  }
}

更优实现建议

  1. 避免硬编码密钥:将my-secure-key存入配置文件(如appsettings.json),通过依赖注入IConfiguration读取,提升安全性和可维护性。
  2. 解耦验证逻辑:将签名验证逻辑封装为独立服务(如ISignatureValidator),通过依赖注入注入到过滤器中,方便单元测试和逻辑复用。
  3. 使用ASP.NET Core授权模型:考虑实现IAuthorizationHandler和自定义授权策略,替代ActionFilter,更符合框架的授权规范,扩展性更强。

示例:封装验证服务的实现

// 定义验证服务接口
public interface ISignatureValidator
{
    bool Validate(string requestBody, string signature, string secretKey);
}

// 实现验证服务
public class SignatureValidator : ISignatureValidator
{
    public bool Validate(string requestBody, string signature, string secretKey)
    {
        // 替换为你的加密验证逻辑
        var computedSignature = EncryptTheBody(requestBody, secretKey);
        return computedSignature == signature;
    }

    private string EncryptTheBody(string body, string key)
    {
        // 你的加密实现
    }
}

// 使用TypeFilterAttribute实现依赖注入的过滤器
public class VerifyAttribute : TypeFilterAttribute
{
    public VerifyAttribute() : base(typeof(VerifyAuthorizationFilter))
    {
    }

    private class VerifyAuthorizationFilter : IAsyncAuthorizationFilter
    {
        private readonly ISignatureValidator _signatureValidator;
        private readonly IConfiguration _configuration;

        public VerifyAuthorizationFilter(ISignatureValidator signatureValidator, IConfiguration configuration)
        {
            _signatureValidator = signatureValidator;
            _configuration = configuration;
        }

        public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            var request = context.HttpContext.Request;
            request.EnableBuffering();

            var signature = request.Headers["x-signature"].FirstOrDefault();
            if (string.IsNullOrEmpty(signature))
            {
                context.Result = new UnauthorizedResult();
                return;
            }

            var requestBody = await new StreamReader(request.Body).ReadToEndAsync();
            request.Body.Position = 0;

            var secretKey = _configuration["SignatureSettings:SecretKey"];
            if (!_signatureValidator.Validate(requestBody, signature, secretKey))
            {
                context.Result = new UnauthorizedResult();
                return;
            }
        }
    }
}

最后在Program.cs中注册服务:

builder.Services.AddScoped<ISignatureValidator, SignatureValidator>();

内容的提问来源于stack exchange,提问作者jb.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:25:17