MVC和.net6,API的body在过滤器中重复消费

在MV中

private async Task<string> ReadPostDataAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            string postData = "";
            var requestStream = await actionContext.Request.Content.ReadAsStreamAsync();
            byte[] bytes = new byte[requestStream.Length];
            requestStream.Read(bytes, 0, bytes.Length);
            //设置当前流的位置为流的开始
            requestStream.Seek(0, SeekOrigin.Begin);

            //将bytes转换为流(生成新的Stream,防止读取一次就被清空数据了)
            Stream newStream = new MemoryStream(bytes);
            using (newStream)
            {
                if (newStream.Length > 0)
                {
                    var byts = new byte[newStream.Length];
                    newStream.Read(byts, 0, (int)byts.Length);
                    postData = Encoding.UTF8.GetString(byts);
                }
            }
            return postData;
        }

 

在.NET6中

context.HttpContext.Request.EnableBuffering();
string bodyStr = string.Empty;
request.EnableBuffering();//允许多次读取HTTP请求的正文
using (var reader = new StreamReader(request.Body,
                                     encoding: Encoding.UTF8,
                                     detectEncodingFromByteOrderMarks: false,
                                     leaveOpen: true)
      )
{
    bodyStr = await reader.ReadToEndAsync();
    request.Body.Position = 0;
}
string secretpost = apisecret + timestamp.ToString() + bodyStr;

 

最后附上MVC和.NET6,WEBAPI加密全部代码

MVC

public class BasicWebApiAuthorize : AuthorizeAttribute
    {
        /// <summary>
        /// 接口加密签名
        /// </summary>
        public string SecretKey = "XXXXXXXXXXXXX";

        public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            try
            {
                var headers = actionContext.Request.Headers;
                if (!TryGetHeaderValue(headers, "timestamp", out string timestamp)
                    || !TryGetHeaderValue(headers, "sign", out string sign))
                {
                    await HandleInvalidRequestAsync(actionContext, "请求Header参数无效", cancellationToken);
                    return;
                }

                if (!ValidateTimestamp(timestamp))
                {
                    await HandleInvalidRequestAsync(actionContext, "时间戳无效", cancellationToken);
                    return;
                }

                string postData = await ReadPostDataAsync(actionContext, cancellationToken);

                if (string.IsNullOrEmpty(postData))
                {
                    await HandleInvalidRequestAsync(actionContext, "请求参数为空", cancellationToken);
                    return;
                }
                if (!ValidateSign(postData, timestamp, sign))
                {
                    await HandleInvalidRequestAsync(actionContext, "签名无效", cancellationToken);
                    return;
                }

                if (!IsUnderAttack(postData))
                {
                    await LogAndSendWorkWxMsg(actionContext, $"存在被攻击内容({postData})");
                    await HandleInvalidRequestAsync(actionContext, "内容无效", cancellationToken);
                    return;
                }
            }
            catch (Exception ex)
            {
                LoggerHelper.Error("授权验证过程中出现错误", ex);
                await HandleServerErrorAsync(actionContext, cancellationToken);
            }

        }
        private async Task<string> ReadPostDataAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            string postData = "";
            var requestStream = await actionContext.Request.Content.ReadAsStreamAsync();
            byte[] bytes = new byte[requestStream.Length];
            requestStream.Read(bytes, 0, bytes.Length);
            //设置当前流的位置为流的开始
            requestStream.Seek(0, SeekOrigin.Begin);

            //将bytes转换为流(生成新的Stream,防止读取一次就被清空数据了)
            Stream newStream = new MemoryStream(bytes);
            using (newStream)
            {
                if (newStream.Length > 0)
                {
                    var byts = new byte[newStream.Length];
                    newStream.Read(byts, 0, (int)byts.Length);
                    postData = Encoding.UTF8.GetString(byts);
                }
            }
            return postData;
        }
        private async Task HandleInvalidRequestAsync(HttpActionContext actionContext, string errorMessage, CancellationToken cancellationToken)
        {
            await LogAndSendWorkWxMsg(actionContext, errorMessage);
            actionContext.Response = await Task.Run(() => getResultContent(400, errorMessage), cancellationToken);
        }
        private bool TryGetHeaderValue(HttpRequestHeaders headers, string key, out string value)
        {
            IEnumerable<string> values;
            if (headers.TryGetValues(key, out values) && values.Any())
            {
                value = values.First();
                return true;
            }
            value = null;
            return false;
        }
        private bool ValidateTimestamp(string timestamp)
        {
            //验证时间戳是否合法
            var timeDate = TimeConvertor.ConvertIntDatetime(Convert.ToDouble(timestamp));
            var diffNow = (DateTime.Now - timeDate).TotalMilliseconds;
            if (diffNow < -60000 || diffNow > 60000)//6秒内有效
            {
                return false;
            }
            // 时间戳验证逻辑...
            return true; // 填充实际的验证逻辑
        }

        private bool ValidateSign(string postData, string timestamp, string sign)
        {
            // 签名验证逻辑...
            //验证签名是否有效
            var _sign = StringHelper.EncryptToMD5Lowercase32($"{postData}{timestamp}{SecretKey}");
            if (_sign.ToLower() != sign.ToLower())
            {
                return false;
            }
            return true; // 填充实际的验证逻辑
        }

        private bool IsUnderAttack(string postData)
        {
            // SQL注入检查逻辑...
            if (postData.ToLower().Contains("select ") || postData.ToLower().Contains("update ") ||
            postData.ToLower().Contains("delete ") || postData.ToLower().Contains("drop "))
            {
                return false;
            }
            return true; // 填充实际的检查逻辑
        }
        private async Task LogAndSendWorkWxMsg(HttpActionContext actionContext, string errorMessage)
        {
            StringBuilder log = new StringBuilder();
            log.AppendLine("接口请求失败:" + errorMessage);
            log.AppendLine($"接口名:{actionContext.Request.RequestUri}");
            LoggerHelper.Error(log.ToString());
        }
        private async Task HandleServerErrorAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            actionContext.Response = await Task.Run(() => getResultContent(500, "服务器内部错误"), cancellationToken);
        }

        private HttpResponseMessage getResultContent(int error_code = 0, string message = "ok")
        {
            var actionResult = new HttpResponseMessage();
            var result = new
            {
                error_code = error_code,
                message = message
            };
            actionResult.Content = new StringContent(JsonConvert.SerializeObject(result), Encoding.GetEncoding("UTF-8"), "application/json");

            return actionResult;
        }
    }

 

.NET6中

public class OpenApiHashaFilter : Attribute, IAsyncAuthorizationFilter
{
    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {

        var request = context.HttpContext.Request;

        //发起请求的时间戳  单位(秒)
        string timestamp = GetHeaderValue(request.Headers, "timestamp");
        //Md5加密后的字符串
        string signature = GetHeaderValue(request.Headers, "signature");

        if (string.IsNullOrEmpty(timestamp) || string.IsNullOrEmpty(signature))
        {
            await HandleUnauthorized(context, "请求缺少重要的header信息");
            return;
        }
        if (!DateTime.TryParse(timestamp, out DateTime parsedTimestamp))
        {
            await HandleUnauthorized(context, "时间戳格式不正确");
            return;
        }

        if (!IsTimestampValid(parsedTimestamp))
        {
            await HandleUnauthorized(context, "接口请求时间超过规定时间");
            return;
        }

        bool isValidSignature = await ValidateRequestAsync(request, timestamp, signature);

        if (!isValidSignature)
        {
            await HandleUnauthorized(context, "接口请求失败,签名错误,无法请求接口");
            return;
        }
    }
    private async Task<bool> ValidateRequestAsync(HttpRequest request, string timestamp, string signature)
    {
        string apisecret = AppSettingHelper.ReadAppSettings("Open", "hmac_secret");
        string data = string.Empty;
        if (request.Method == HttpMethods.Get)
        {
            data = request.GetEncodedUrl();
        }
        else
        {
            request.EnableBuffering();

            using (var reader = new StreamReader(request.Body,
                                                 encoding: Encoding.UTF8,
                                                 detectEncodingFromByteOrderMarks: false,
                                                 leaveOpen: true))
            {
                data = await reader.ReadToEndAsync();
                request.Body.Position = 0;
            }
        }

        return ValidateSignature(timestamp, signature, apisecret, data);
    }

    private static string GetHeaderValue(IHeaderDictionary headers, string key)
    {
        if (headers.TryGetValue(key, out var values) && values.Any())
        {
            return HttpUtility.UrlDecode(values.FirstOrDefault());
        }
        return null;
    }

    private async Task HandleUnauthorized(AuthorizationFilterContext context, string errorMessage)
    {
        context.Result = new ObjectResult(new { error = "Unauthorized", errorMessage })
        {
            StatusCode = StatusCodes.Status401Unauthorized
        };
        await Task.CompletedTask;
    }
    private bool IsTimestampValid(DateTime timestamp)
    {
        TimeSpan timeSpan = DateTime.UtcNow - timestamp.ToUniversalTime();
        return timeSpan.TotalSeconds <= 120; // 考虑两分钟内的有效时间窗口
    }

    private bool ValidateSignature(string timestamp, string signature, string secretKey, string data)
    {
        string secretpost = $"{secretKey}{timestamp}{data}";
        string calculatedMd5 = SecretHepler.EncryptToMD5Lowercase32(secretpost);

        return calculatedMd5.ToUpper() == signature.ToUpper();
    }
}

 

posted @ 2024-03-21 10:10  寒风中亦温暖  阅读(35)  评论(0)    收藏  举报