加入收藏 | 设为首页 | 会员中心 | 我要投稿 莱芜站长网 (https://www.0634zz.com/)- 云连接、建站、智能边缘云、设备管理、大数据!
当前位置: 首页 > 编程开发 > asp.Net > 正文

.NET API 接口数据传输加密最佳实践记录

发布时间:2023-02-17 10:11:41 所属栏目:asp.Net 来源:互联网
导读:我们在做 Api 接口时,相信一定会有接触到要给传输的请求 body 的内容进行加密传输。其目的就是为了防止一些敏感的内容直接被 UI 层查看或篡改。 其实粗略一想就能想到很多种方案,但是哪些方案是目前最适合我们项目的呢? 硬编码方式 最先想到的应该就是硬
  我们在做 Api 接口时,相信一定会有接触到要给传输的请求 body 的内容进行加密传输。其目的就是为了防止一些敏感的内容直接被 UI 层查看或篡改。
 
  其实粗略一想就能想到很多种方案,但是哪些方案是目前最适合我们项目的呢?
 
  硬编码方式
  最先想到的应该就是硬编码方式,就是哪个接口需要进行传输加密,那么就针对该接口特殊处理:

  public class SecurityApiController {
      ...
      public async Task<Result> UpdateUser([FromBody] SecurityRequest request) {
          var requestBody = RsaHelper.Decrypt(privateKey, request.Content);
              var user = JsonHelper.Deserialize<UserDto>(requestBody);
          await UpdateUserAsync(user);
          return new Result(RsaHelper.Encrypt(publicKey, new{ Success=true}));
      }
  }
  这种方式好处是简单明了,按需编程即可,不会对其它接口造成污染。
 
  一旦这种需求越来越多,我们就会写大量如上的重复性代码;而对于前端而言也是如此,所以当我们需要传输加密乃是最基础的需求时,上面硬编码的方式就显得很不合适了。
 
  这个时候我们可以采用统一入口的方式来实现
 
 
  统一入口
  回顾上面的硬编码方式,其实每个接口处的加解密处理从 SRP 原则上理解,不应该是接口的职责。所以需要把这部分的代码移到一个单独的方法,再加解密之后我们再把该请求调度到具体的接口。
 
  这种方式其实有很多种实现方式,在这里我先说一下我司其中一个 .NET4.5 的项目采取的方式。
 
  其实就是额外提供了一个统一的入口,所有需要传输加密的需求都走这一个接口:如http://api.example.com/security

  public class SecurityController {
      ...
      public async Task<object> EntryPoint([FromBody] SecurityRequest request) {
          var requestBody = RsaHelper.Decrypt(privateKey, request.Content);
          var user = JsonHelper.Deserialize<UserDto>(requestBody);
          var obj = await DispathRouter(requestBody.Router, user);
              return new Result(RsaHelper.Encrypt(publicKey, obj));
      }
       
      public async Task<object> DispathRouter(Router router, object body) {
          ...
          Type objectCon = typeof(BaseController);
              MethodInfo methInfo = objectCon.GetMethod(router.Name);
              var resp = (Task<object>)methInfo.Invoke(null, body);
              return await resp;
      }
  }
  很明显这是通过统一入口地址调用并配合反射来实现这种目的。
 
  这种好处如前面所说,统一了调用入口,这样提高了代码复用率,让加解密不再是业务接口的一部分了。同样,这种利用一些不好的点;比如用了反射性能会大打折扣。并且我们过度的进行统一了。我们看到这种方式只能将所有的接口方法都写到 BaseController。所以我司项目的 Controller 部分,会看到大量如下的写法:

  // 文件 UserController.cs
  public partial class BaseController {
      ...
  }
  // 文件 AccountController.cs
  public partial class BaseController {
  
  }
  // ...
  这样势必就会导致一个明显的问题,就是“代码爆炸”。这相当于将所有的业务逻辑全部灌输到一个控制器中,刚开始写的时候方便了,但是后期维护以及交接换人的时候阅读代码是非常痛苦的一个过程。因为在不同的 Controller 文件中势必会重复初始化一些模块,而我们在引用方法的时候 IDE 每次都会显示上千个方法,有时候还不得不查看哪些方法名一样或相近的具体意义。
 
  针对上述代码爆炸的方式还有一种优化,就是将控制器的选择开放给应用端,也就是将方法名和控制器名都作为请求参数暴露给前端,但是这样会加大前端的开发心智负担。
 
  综上所述我是非常不建议采用这种方式的。虽说是很古老的.Net4/4.5 的框架,但是我们还是有其它相对更优雅的实现方式。
 
 
  中间件
  其实我们熟悉了 .NETCore 下的 Middleware机制,我们会很容易的在 .NETCore 下实现如标题的这种需求:

  // .NET Core 版本
  public class SecuriryTransportMiddleware {
      private readonly RequestDelegate _next;
  
      public RequestCultureMiddleware(RequestDelegate next)
      {
          _next = next;
      }
       
      public async Task InvokeAsync(HttpContext context)
      {
          // request handle
          var encryptedBody = context.Request.Body;
          var encryptedContent = new StreamReader(encryptedBody).ReadToEnd();
          var decryptedBody = RsaHelper.Decrypt(privateKey, encryptedContent);
          var originBody = JsonHelper.Deserialize<object>(decryptedBody);
           
          var json = JsonHelper.Serialize(dataSource);
          var requestContent = new StringContent(json, Encoding.UTF8, "application/json");
          stream = await requestContent.ReadAsStreamAsync();
          context.Request.Body = stream;
           
          await _next(context);
          // response handle
          var originContent = new StreamReader(context.Response.Body).ReadToEnd();
          var encryptedBody = RsaHelper.Encrypt(privateKey, originContent);
          var responseContent = new StringContent(json, Encoding.UTF8, "application/json");
          context.Response.Body = await responseContent.ReadAsStreamAsync();
          // 或者直接
          // await context.Response.WriteAsync(encryptedBody);
      }
  }
  为了方便描述,以上代码我省略了必要的校验和异常错误处理
 
  这样有什么好处呢?一个最明显的好处就是解耦了加解密与真正业务需求。对真正的业务代码几乎没有侵略性。其实我认为业务开发能做到这里其实就差不多了,还有其它需求都可以基于这个中间件进行拓展开发。
 
  那么在 .NET Framwork 没有中间件怎么办呢?
 
  其实在 .NET Framwork 框架下 IHttpModule 能和中间件一样能做到这点:

  public class SecurityTransportHttpModule : IHttpModule {
      ...
      public void Init(HttpApplication context) {
          ...
          context.BeginRequest += ContextBeginRequest;
          context.PostRequestHandlerExecute += ContextPostRequestHandlerExecute;

(编辑:莱芜站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读