.NET API 接口數據傳輸加密最佳實踐

.NET API 接口數據傳輸加密最佳實踐我們在做 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.cspublic partial class BaseController { ...}// 文件 AccountController.cspublic 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 handlevar 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 handlevar 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);}}

推薦閱讀