zoukankan      html  css  js  c++  java
  • Net Core 自定义 Middleware 加密解密(更新)

    前言:第一次写文章,有问题请轻喷

    当前使用 Net Core 版本 2.1.3

    我们经常在开发中需要把实体的主键 Id 传输到前端,但是在Get的时候又不想让前端能看到明文,我们通常会加密这些数据,所以有了这篇文章来写一些心得。(主要是我在网上找的代码写得太简单了,不符合我的需求)

    这里我用的是 Net Core 自带的 DataProtector ,使用方式自行百度一下

    关于中间件 Middleware 可以看看博园大佬写的,太多就不列举了,官方文档:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/index?view=aspnetcore-2.2

    一张图来概括就是这样:

    1. 中间件完整代码

    public class DataProtectMiddleware
        {
            private readonly RequestDelegate _next;
            private readonly IDataProtector _dataProtector;
    
            public DataProtectMiddleware(RequestDelegate requestDelegate, IDataProtectionProvider dataProtection)
            {
                _dataProtector = dataProtection.CreateProtector("defaultProtector");
                _next = requestDelegate;
            }
    
            private static readonly string _matchJsonIdExpression = ""[a-zA-Z0-9]+id"";
            private static readonly string _matchJsonIdValueExpression = ""[a-zA-Z0-9_\-]+"";
    
            private static readonly string _matchQueryIdExpression = "[a-zA-Z0-9]+id";
            private static readonly string _matchQueryIdValueExpression = "[a-zA-Z0-9_\-]+";
    
            private Regex _matchJsonIdKeyValue = new Regex($"{_matchJsonIdExpression}:{_matchJsonIdValueExpression}", RegexOptions.IgnoreCase);
            private Regex _matchQueryIdKeyValue = new Regex($"{_matchQueryIdExpression}={_matchQueryIdValueExpression}", RegexOptions.IgnoreCase);
    
            public async Task Invoke(HttpContext context)
            {
                // 替换原本的 Response.Body 流在 _next(context) 执行下一个中间件后,需要读取数据,原本的流不可读 canReader = false
                var originalResponseStream = context.Response.Body;
                using var replaceResponseStream = new MemoryStream();
    
                context.Response.Body = replaceResponseStream;
    
                // 过滤请求
                await FilterRequest(context);
    
                await _next(context);
    
                if (context.Response.StatusCode == StatusCodes.Status204NoContent)
                    return;
    
                // 过滤响应
                await FilterResponse(context, originalResponseStream, replaceResponseStream);
            }
    
            private async Task FilterResponse(HttpContext context, Stream originalResponseStream, MemoryStream replaceResponseStream)
            {
                var responseData = new StringBuilder();
    
                using (var reader = new StreamReader(replaceResponseStream))
                {
                    context.Response.Body.Seek(0, SeekOrigin.Begin);
    
                    responseData = new StringBuilder(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(""", "");
                        var protectId = Regex.Replace(itemMathId.Value,
                            $"{_matchJsonIdValueExpression}$",
                            $""{_dataProtector.Protect(unprotectId)}"");
    
                        responseData = responseData.Replace(itemMathId.Value, protectId);
                    }
                }
    
                // 将返回的 Response 流 Copy 到原始流
                await originalResponseStream.WriteAsync(Encoding.Default.GetBytes(responseData.ToString()));
    
                context.Response.Body = originalResponseStream;
            }
    
            private async Task<HttpContext> FilterRequest(HttpContext context)
            {
                // 可以考虑反序列化为对象,更加灵活控制加密字段,这里使用正则因为 简单,粗暴,快 反射要慢一点
                var requestData = new StringBuilder();
    
                // 过滤 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());
                }
    
                if (context.Request.Method.Equals(HttpMethods.Post, StringComparison.CurrentCultureIgnoreCase))
                {
                    // 过滤 Post 请求 Body Stream 中的 Id 值,并解密
                    using (var reader = new StreamReader(context.Request.Body))
                    {
                        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);
                        }
                    }
    
                    var requestStringContent = new StringContent(requestData.ToString());
    
                    context.Request.Body = await requestStringContent.ReadAsStreamAsync();
                }
    
                return context;
            }
        }

    整体执行流程图

    写在最后:整个项目就不发上去了,帮朋友写的一个小玩意,这个类文件我发布到百度网盘把。

    链接: https://pan.baidu.com/s/1m72tHkw8zAzYYpWO0Yw2FQ 提取码: r3qh 

  • 相关阅读:
    编程语言学哪个比较好?
    C#一定要避免程序中很多的依靠
    EXPIREAT
    EXISTS
    DUMP
    Python之sys模块
    Python的OS模块
    CentOS 7上安装gitlab-runner
    PyCharm激活方法
    Linux03 文件的相关操作(touch、rm、mv、cat)
  • 原文地址:https://www.cnblogs.com/luciusliang/p/11526225.html
Copyright © 2011-2022 走看看