延续上一篇 Net Core 自定义 Middleware 加密解密 做重构,以及BUG修复
首先是启用 EnableBuffering() 关于 EnableBuffering 的官方文档 https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.httprequestrewindextensions.enablebuffering?view=aspnetcore-3.0
主要作用是启用多次读取 Request.Body 以及对 Request.Body 内容设置多大的请求体需要存储到磁盘,具体看文档内容,这里只用到多次读取 Reqeust.Body。
本次做了代码精简以及修复 Response.Body 没有替换回原始 Stream 的 BUG(自己找出来的。。。)
public async Task Invoke(HttpContext context) { context.Request.EnableBuffering(); _stopwatch.Restart(); var requestData = new StringBuilder(); var responseData = new StringBuilder(); _matchJsonIdKeyValue = new Regex($"{_matchJsonIdExpression}:{_matchJsonIdValueExpression}", RegexOptions.IgnoreCase); _matchQueryIdKeyValue = new Regex($"{_matchQueryIdExpression}={_matchQueryIdValueExpression}", RegexOptions.IgnoreCase); // 过滤请求 await FilterRequest(context, context.Request.Body, requestData); _stopwatch.Stop(); Console.WriteLine($"-------------------- Request 解密用时:{_stopwatch.ElapsedMilliseconds}毫秒 --------------------"); // 将原本的 Response Stream 放入局部变量,因为原本的流不可读 var originStream = context.Response.Body; var replaceStream = new MemoryStream(); // 替换原本的流 context.Response.Body = replaceStream; // 执行下一个中间件 await _next(context); _stopwatch.Restart(); // 过滤响应 await FilterResponse(replaceStream, responseData); replaceStream.Dispose(); // 将解密数据写入原本的 Response 流中 using (var writer = new StreamWriter(originStream)) { await writer.WriteAsync(responseData.ToString()); await writer.FlushAsync(); } // 替换回原本的 Stream context.Response.Body = originStream; _stopwatch.Stop(); Console.WriteLine($"======================= 数据加密过程费时:{_stopwatch.ElapsedMilliseconds} 毫秒 ======================="); }
private async Task FilterResponse(Stream responseStream, StringBuilder responseData) { using (var reader = new StreamReader(responseStream)) { responseStream.Position = 0; responseData.Append(await reader.ReadToEndAsync()); // 筛选以Id结尾的字段,并将ID加密 var matchedIdCollection = _matchJsonIdKeyValue.Matches(responseData.ToString()); foreach (Match itemMathId in matchedIdCollection) { var unprotectId = Regex.Match(itemMathId.Value, $"{_matchJsonIdValueExpression}$").Value.Replace("\"", string.Empty); var protectId = Regex.Replace(itemMathId.Value, $"{_matchJsonIdValueExpression}$", $"\"{_dataProtector.Protect(unprotectId)}\""); responseData = responseData.Replace(itemMathId.Value, protectId); } } }
private async Task FilterRequest(HttpContext context, Stream currentRequest, StringBuilder requestData) { // 可以考虑反序列化为对象,更加灵活控制加密字段,这里使用正则因为 简单,粗暴,快 反射要慢一点 // 过滤 Get 请求中 QueryString 的 Id 值,并解密 if (context.Request.Method.Equals(HttpMethods.Get, StringComparison.CurrentCultureIgnoreCase)) { requestData.Append(context.Request.QueryString); var matchedIdCollection = _matchQueryIdKeyValue.Matches(requestData.ToString()); foreach (Match itemMathId in matchedIdCollection) { var protectId = Regex.Match(itemMathId.Value, $"{_matchQueryIdValueExpression}$").Value; var unprotectId = Regex.Replace(itemMathId.Value, $"{_matchQueryIdValueExpression}$", $"{_dataProtector.Unprotect(protectId)}"); requestData = requestData.Replace(itemMathId.Value, unprotectId); } context.Request.QueryString = new QueryString(requestData.ToString()); } // 过滤 Post 请求 Body Stream 中的 Id 值,并加密 if (context.Request.Method.Equals(HttpMethods.Post, StringComparison.CurrentCultureIgnoreCase)) { // 保持这个流打开,不然会导致后续中间件无法打开 Request.Body 流 using (var reader = new StreamReader(currentRequest, leaveOpen:true)) { requestData.Append(await reader.ReadToEndAsync()); var matchedIdCollection = _matchJsonIdKeyValue.Matches(requestData.ToString()); foreach (Match itemMathId in matchedIdCollection) { var protectId = Regex.Match(itemMathId.Value, $"{_matchJsonIdValueExpression}$").Value.Replace("\"", ""); var unprotectId = Regex.Replace(itemMathId.Value, $"{_matchJsonIdValueExpression}$", $"\"{_dataProtector.Unprotect(protectId)}\""); requestData = requestData.Replace(itemMathId.Value, unprotectId); } // 重置读取位置 currentRequest.Seek(0, SeekOrigin.Begin); } } }