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

.NET Core 7 API端到端加密:请求解密最佳实践咨询

.NET Core 7 API端到端加密:两种实现方案的最佳实践

针对你提出的两种端到端加密实现方式,以下是具体的最佳实践分析和代码示例:

方案1:自定义过滤器统一处理请求解密

可行性与优势

完全可行,这种方式遵循关注点分离原则,将加解密逻辑与控制器业务代码解耦,适合全局或批量接口的加解密需求,能大幅减少重复代码,便于后续维护。

实现步骤与代码示例

  1. 封装加密解密服务
    先实现通用的加密解密逻辑,这里以AES算法为例:

    public interface IEncryptionService
    {
        string Decrypt(string encryptedText, string key = null);
        string Encrypt(string plainText, string key = null);
    }
    
    public class AesEncryptionService : IEncryptionService
    {
        private readonly string _defaultKey;
    
        public AesEncryptionService(IConfiguration config)
        {
            _defaultKey = config["Encryption:DefaultKey"];
            // 生产环境建议使用密钥管理服务(如Azure Key Vault)存储密钥,避免硬编码
        }
    
        public string Decrypt(string encryptedText, string key = null)
        {
            var actualKey = key ?? _defaultKey;
            using var aes = Aes.Create();
            aes.Key = Encoding.UTF8.GetBytes(actualKey.PadRight(32).Substring(0, 32));
            aes.IV = Encoding.UTF8.GetBytes(actualKey.PadRight(16).Substring(0, 16));
            
            var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
            var encryptedBytes = Convert.FromBase64String(encryptedText);
            
            using var ms = new MemoryStream(encryptedBytes);
            using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
            using var sr = new StreamReader(cs);
            return sr.ReadToEnd();
        }
    
        public string Encrypt(string plainText, string key = null)
        {
            var actualKey = key ?? _defaultKey;
            using var aes = Aes.Create();
            aes.Key = Encoding.UTF8.GetBytes(actualKey.PadRight(32).Substring(0, 32));
            aes.IV = Encoding.UTF8.GetBytes(actualKey.PadRight(16).Substring(0, 16));
            
            var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
            using var ms = new MemoryStream();
            using var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
            using var sw = new StreamWriter(cs);
            
            sw.Write(plainText);
            sw.Flush();
            cs.FlushFinalBlock();
            return Convert.ToBase64String(ms.ToArray());
        }
    }
    
  2. 实现自定义请求解密过滤器
    重写ActionFilterAttribute的OnActionExecutingAsync方法,读取并解密请求体,替换控制器的参数:

    public class RequestDecryptionFilter : ActionFilterAttribute
    {
        private readonly IEncryptionService _encryptionService;
        private readonly ILogger<RequestDecryptionFilter> _logger;
    
        public RequestDecryptionFilter(IEncryptionService encryptionService, ILogger<RequestDecryptionFilter> logger)
        {
            _encryptionService = encryptionService;
            _logger = logger;
        }
    
        public override async Task OnActionExecutingAsync(ActionExecutingContext context)
        {
            // 仅处理带请求体的HTTP方法
            if (!new[] { HttpMethods.Post, HttpMethods.Put, HttpMethods.Patch }.Contains(context.HttpContext.Request.Method))
            {
                await base.OnActionExecutingAsync(context);
                return;
            }
    
            try
            {
                // 启用请求体缓冲,允许多次读取
                context.HttpContext.Request.EnableBuffering();
                using var reader = new StreamReader(context.HttpContext.Request.Body, leaveOpen: true);
                var encryptedBody = await reader.ReadToEndAsync();
                context.HttpContext.Request.Body.Position = 0; // 重置流位置,避免后续组件读取失败
    
                // 解密并反序列化为目标模型
                var decryptedBody = _encryptionService.Decrypt(encryptedBody);
                var targetParam = context.ActionArguments.Values.FirstOrDefault();
                if (targetParam != null)
                {
                    var modelType = targetParam.GetType();
                    var deserializedModel = JsonSerializer.Deserialize(decryptedBody, modelType);
                    // 替换ActionArguments中的参数,控制器将直接拿到解密后的模型
                    var paramKey = context.ActionArguments.Keys.First(k => context.ActionArguments[k] == targetParam);
                    context.ActionArguments[paramKey] = deserializedModel;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "请求解密失败");
                context.Result = new BadRequestObjectResult("请求数据无效或解密失败");
            }
    
            await base.OnActionExecutingAsync(context);
        }
    }
    
  3. 注册服务与过滤器
    在Program.cs中注册加密服务和过滤器:

    builder.Services.AddScoped<IEncryptionService, AesEncryptionService>();
    
    // 全局注册(所有接口生效)
    builder.Services.AddControllers(options =>
    {
        options.Filters.Add<RequestDecryptionFilter>();
    });
    
    // 或局部注册:在控制器/Action上添加[ServiceFilter(typeof(RequestDecryptionFilter))]
    

关键注意事项

  • 必须启用请求体缓冲,否则Request.Body只能读取一次,后续组件会读取失败
  • 完善异常处理,解密失败直接返回BadRequest,避免泄露敏感信息
  • 若不同接口使用不同密钥,可从请求头中获取密钥,传入解密方法

方案2:控制器层直接调用加解密层

实现方式

在控制器的Action中手动读取加密请求体,调用加密服务解密后反序列化为模型;返回响应时,先序列化模型再加密返回。

代码示例

[ApiController]
[Route("api/data")]
public class DataController : ControllerBase
{
    private readonly IEncryptionService _encryptionService;
    private readonly ILogger<DataController> _logger;

    public DataController(IEncryptionService encryptionService, ILogger<DataController> logger)
    {
        _encryptionService = encryptionService;
        _logger = logger;
    }

    [HttpPost]
    public async Task<IActionResult> ProcessData()
    {
        try
        {
            // 读取并解密请求体
            using var reader = new StreamReader(Request.Body);
            var encryptedBody = await reader.ReadToEndAsync();
            var decryptedBody = _encryptionService.Decrypt(encryptedBody);
            var requestModel = JsonSerializer.Deserialize<DataRequest>(decryptedBody);

            // 执行业务逻辑
            var responseModel = new DataResponse
            {
                Status = "Success",
                Data = requestModel
            };

            // 加密响应并返回
            var serializedResponse = JsonSerializer.Serialize(responseModel);
            var encryptedResponse = _encryptionService.Encrypt(serializedResponse);
            return Ok(encryptedResponse);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "请求处理失败");
            return BadRequest("请求数据无效");
        }
    }
}

// 模型定义
public class DataRequest
{
    public string Id { get; set; }
    public string Content { get; set; }
}

public class DataResponse
{
    public string Status { get; set; }
    public DataRequest Data { get; set; }
}

优缺点

  • 优点:灵活性极高,可针对单个接口定制加解密逻辑,无需全局配置
  • 缺点:代码重复严重,多个接口需要加解密时会产生大量冗余代码,不符合DRY原则

方案选择建议

  • 若大部分接口都需要加解密,优先选择方案1(自定义过滤器),统一管理逻辑,降低维护成本
  • 若仅少数接口需要加解密,或不同接口的加解密逻辑差异较大,选择方案2更合适

额外优化建议

  • 响应加密也可通过过滤器实现(重写OnActionExecutedAsync方法),统一处理所有响应的加密
  • 推荐使用RSA交换AES密钥的模式:Android端与API端先通过RSA交换临时AES密钥,再用AES加密实际业务数据,兼顾安全性与性能
  • 密钥务必通过安全方式管理,禁止硬编码到代码中

内容的提问来源于stack exchange,提问作者abid Hussain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 07:35:21