电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

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

瀏覽:50日期:2022-06-09 11:07:44
目錄
  • 硬編碼方式
  • 統一入口
  • 中間件
  • 模型綁定
  • 總結
  • 參考資料

我們在做 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;
	}
	
	private void ContextBeginRequest(object sender, EventArgs e) {
	    HttpContext context = ((HttpApplication)sender).Context;
	    var encryptedBody = context.Request.Body;
	    ...
	    context.Request.Body = stream;
	}
	
	private void ContextPostRequestHandlerExecute(object sender, EventArgs e)
{
    HttpContext context = ((HttpApplication)sender).Context;
    ...
    context.Response.Write(encryptedBody)
}
}

為什么之前提到這種方案就“差不多”了呢,實際上上面這種方式在某些場景下會顯得比較“累贅”。因為無論通過中間件和還是 IHttpModule 都會導致所有請求都會經過它,相當于增加了一個過濾器,如果這時候我要新增一個上傳文件接口,那必然也會經過這個處理程序。說的更直接一點,如果碰到那些少數不需要加解密的接口請求那要怎么辦呢?

其實上面可以進行拓展處理,比如對特定的請求進行過濾:

if(context.Request.Path.Contains("upload")) {
	return;
}

注意上述代碼只是做個 demo 展示,真正還是需要通過如 context.GetRouterData() 獲取路由數據進行精準比對。

當類似于這種需求開始變多以后(吐槽:誰知道業務是怎么發展的呢?)原來的中間件的“任務量”開始變得厚重了起來。到時候也會變得難以維護和閱讀。

這個時候就是我目前較為滿意的解決方案登場了,它就是模型綁定 ModelBinding。

模型綁定

回到需求的開端,不難發現,我們其實要是如何將前端加密后的請求體綁定到我們編寫的接口方法中。這里面的過程很復雜,需要解析前端發起的請求,解密之后還要反序列化成目標接口需要的方法參數。而這個過程還要伴隨著參數校驗,如這個請求是否符合加密格式。而這個過程的一切都是模型綁定要解決的事。我們以 NETCore 版本為例子,講一下大概的流程;

模型綁定的過程其實就是將請求體的各個字段于具體的 CLR 類型的字段屬性進行一一匹配的過程。.NetCore 再程序啟動時會默認提供了一些內置的模型綁定器,并開放 IModelBinderProvider 接口允許用戶自定義模型綁定器。我們通過查看 MvcCoreMvcOptionsSetup 就清楚看到框架為我們添加 18 個自帶的模型綁定器。以及如何調用的方式。

所以接下來我們很容易的可以一葫蘆畫瓢的照抄下來:

public class SecurityTransportModelBinder : IModelBinder {
    ...
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
if (bindingContext == null)
{
    throw new ArgumentNullException(nameof(bindingContext));
}

try
{
    var request = bindingContext.HttpContext.Request;
    var model = await JsonSerializer.DeserializeAsync<SafeDataWrapper>(request.Body, new JsonSerializerOptions
    {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    });
    var decryptContent = RsaHelper.Decrypt(model.Info, privateKey);
    var activateModel = JsonSerializer.Deserialize(decryptContent, bindingContext.ModelMetadata.ModelType, new JsonSerializerOptions
    {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    });
    //重新包裝
    if (activateModel == null)
    {
bindingContext.Result = ModelBindingResult.Failed();
    }
    else
    {
bindingContext.Result = ModelBindingResult.Success(activateModel);
    }
}
catch (Exception exception)
{
    bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
exception,
bindingContext.ModelMetadata);
}

_logger.DoneAttemptingToBindModel(bindingContext);
//return Task.CompletedTask;
    }
}

抄了 ModelBinder 還不行,還要抄 ModelBinderProvider:

public class SecurityTransportModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
if (context == null)
{
    throw new ArgumentNullException(nameof(context));
}

if (context.Metadata.IsComplexType && typeof(IApiEncrypt).IsAssignableFrom(context.Metadata.ModelType))
{
    var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
    var configuration = context.Services.GetRequiredService<IConfiguration>();
    return new SecurityTransportModelBinder(loggerFactory, configuration);
}

return null;
    }
}

這里我做了一個方便我自己的拓展功能,就是顯示打了 IApiEncrypt 接口標簽的才會正常進行解析綁定。

剩下的就是在 ConfigureService 中添加進去即可:

services.AddControllers(options => {
	...
	options.ModelBinderProviders.Insert(0, new SecurityTransportModelBinderProvider());
})

這樣實現過后,我們就能像使用 FromBody 那樣就能按需調用即可:

[HttpPost("security")]
public async Task<ResultDto> DemoDecrypt([ModelBinder(typeof(SecurityTransportModelBinder))] OriginBusinessRequest request)
{
    //激活結果
	...
    return await Task.FromResult(WriteSafeData(data, publicKey));
}

如果是默認處理加解密也是可以的,直接在對應的請求實體類打上 IApiEncrypt 標簽就會自動執行模型綁定

public class UserUpdateRequest: IApiEncrypt {
	public int UserId { get; set; }
	public string Phone { get; set; }
	public string Address { get; set; }
	...
}

這種方案其實也還是有缺點的,從剛剛的使用來看就知道,模型綁定無法解決返回自動加密處理。所以我們不得不在每個接口處寫下如 WriteSafeData(data, publicKey) 這種顯式加密的代碼。

優化的方式也很簡單,其實我們可以通過過濾器可以解決,這也是為什么我要加 IApiEncrypt 的原因,因為有了這個就能確定知道這是一個安全傳輸的請求,進而進行特殊處理。

注意,這不是 .NET Core 獨有的特性,.Net Framework 也有模型綁定器

總結

針對接口級傳輸加密這個需求,我們總共討論了四種實現方式。其實各有各的好處和缺點。

硬編碼方式適合只有特定需求的場景下是適合這種方案的。但是一旦這種需求成為一種規范或普遍場景時,這個方法就不合適了

統一入口適合制定了一種接口規范,所有加密的請求都走這一個接口。然后通過路由解析調度到不同的控制器。其實我講了我司在 .NET Framework 下采取的一種方案,好處時實現簡單,做到了代碼復用,對業務代碼進行了解耦。但缺點是用了反射成為了性能消耗點,并且當業務越來越多就會產生代碼爆炸,成為了維護災難。

而中間件或 IHttpModule 方式就很好的解決這了這點。但同樣也不是使用所有場景,如對新增的不需要加密的接口要進行過濾處理等。

最后介紹了模型綁定這種方式,技能很方便的滿足大多數場景,也滿足個別列外的需求。但同樣也有缺點,就是無法針對接口響應體進行自動加密處理,所謂好事做到底,送佛送到西,這事只能算是做到一半吧。

其實我還想到了一種類似 Aop 的方案,那就是實現一個路由過濾器的功能,當請求進來,通過路由處理程序對請求體進行解密,然后重寫請求流。然后調度到對應的原始路由,最后在響應的時候再加密重寫一次。不過查閱了一番資料,并沒有收獲。

參考資料

  • https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.modelbinding.imodelbinderprovider?view=aspnetcore-7.0
  • https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-6.0
  • https://www.stevejgordon.co.uk/html-encode-string-aspnet-core-model-binding

到此這篇關于.NET API 接口數據傳輸加密最佳實踐記錄的文章就介紹到這了,更多相關.net api接口數據傳輸加密內容請搜索以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持!

標簽: ASP.NET
主站蜘蛛池模板: 深圳市索富通实业有限公司-可燃气体报警器 | 可燃气体探测器 | 气体检测仪 | 楼梯定制_楼梯设计施工厂家_楼梯扶手安装制作-北京凌步楼梯 | 爱佩恒温恒湿测试箱|高低温实验箱|高低温冲击试验箱|冷热冲击试验箱-您身边的模拟环境试验设备技术专家-合作热线:400-6727-800-广东爱佩试验设备有限公司 | 河北码上网络科技|邯郸小程序开发|邯郸微信开发|邯郸网站建设 | 热处理温控箱,热处理控制箱厂家-吴江市兴达电热设备厂 | 江苏南京多语种翻译-专业翻译公司报价-正规商务翻译机构-南京华彦翻译服务有限公司 | 食药成分检测_调料配方还原_洗涤剂化学成分分析_饲料_百检信息科技有限公司 | 杭州|上海贴标机-百科 | 东莞ERP软件_广州云ERP_中山ERP_台湾工厂erp系统-广东顺景软件科技有限公司 | 色谱柱-淋洗液罐-巴罗克试剂槽-巴氏吸管-5ml样品瓶-SBS液氮冻存管-上海希言科学仪器有限公司 | 神马影院-实时更新秒播| 电缆故障测试仪_电缆故障定位仪_探测仪_检测仪器_陕西意联电气厂家 | 本安接线盒-本安电路用接线盒-本安分线盒-矿用电话接线盒-JHH生产厂家-宁波龙亿电子科技有限公司 | 带式压滤机_污泥压滤机_污泥脱水机_带式过滤机_带式压滤机厂家-河南恒磊环保设备有限公司 | 一体化隔油提升设备-餐饮油水分离器-餐厨垃圾处理设备-隔油池-盐城金球环保产业发展有限公司 | Type-c防水母座|贴片母座|耳机接口|Type-c插座-深圳市步步精科技有限公司 | 防火卷帘门价格-聊城一维工贸特级防火卷帘门厂家▲ | 分轨 | 上传文件,即刻分离人声和伴奏 | 帽子厂家_帽子工厂_帽子定做_义乌帽厂_帽厂_制帽厂_帽子厂_浙江高普制帽厂 | 立式矫直机_卧式矫直机-无锡金矫机械制造有限公司 | 上海租奔驰_上海租商务车_上海租车网-矢昂汽车服务公司 | 阁楼货架_阁楼平台_仓库仓储设备_重型货架_广州金铁牛货架厂 | 大倾角皮带机-皮带输送机-螺旋输送机-矿用皮带输送机价格厂家-河南坤威机械 | 钢托盘,钢制托盘,立库钢托盘,金属托盘制造商_南京飞天金属制品实业有限公司 | 南京兰江泵业有限公司-水解酸化池潜水搅拌机-絮凝反应池搅拌机-好氧区潜水推进器 | 杭州顺源过滤机械有限公司官网-压滤机_板框压滤机_厢式隔膜压滤机厂家 | 胀套-锁紧盘-风电锁紧盘-蛇形联轴器「厂家」-瑞安市宝德隆机械配件有限公司 | 米顿罗计量泵(科普)——韬铭机械| MES系统工业智能终端_生产管理看板/安灯/ESOP/静电监控_讯鹏科技 | 郑州律师咨询-郑州律师事务所_河南锦盾律师事务所 | 地脚螺栓_材质_标准-永年县德联地脚螺栓厂家| 青岛侦探_青岛侦探事务所_青岛劝退小三_青岛婚外情取证-青岛王军侦探事务所 | KBX-220倾斜开关|KBW-220P/L跑偏开关|拉绳开关|DHJY-I隔爆打滑开关|溜槽堵塞开关|欠速开关|声光报警器-山东卓信有限公司 | 日本细胞免疫疗法_肿瘤免疫治疗_NK细胞疗法 - 免疫密码 | 无锡网站建设-做网站-建网站-网页设计制作-阿凡达建站公司 | IHDW_TOSOKU_NEMICON_EHDW系列电子手轮,HC1系列电子手轮-上海莆林电子设备有限公司 | 共享雨伞_共享童车_共享轮椅_共享陪护床-共享产品的领先者_有伞科技 | 干粉砂浆设备_干混砂浆生产线_腻子粉加工设备_石膏抹灰砂浆生产成套设备厂家_干粉混合设备_砂子烘干机--郑州铭将机械设备有限公司 | 礼仪庆典公司,礼仪策划公司,庆典公司,演出公司,演艺公司,年会酒会,生日寿宴,动工仪式,开工仪式,奠基典礼,商务会议,竣工落成,乔迁揭牌,签约启动-东莞市开门红文化传媒有限公司 | 制氮设备_PSA制氮机_激光切割制氮机_氮气机生产厂家-苏州西斯气体设备有限公司 | 广州展览制作工厂—[优简]直营展台制作工厂_展会搭建资质齐全 |