有时,有必要在浏览器中运行的应用程序与运行浏览器的同一系统上运行的程序之间传输数据。 例如,如果我们需要使用本地连接的设备,则可能有必要。 智能卡读取器,硬件加密密钥等。

图片从这里
首先想到的是解决此问题的三种方法:
- 使用浏览器工具或为其编写插件
- 通过后端组织数据交换,充当中介
- 将HTTP服务添加到程序,然后直接从浏览器访问它
第三项看起来不错,允许您删除程序中的授权,根本不需要任何用户界面。 让我们尝试通过在.NET Framework 4下用C#编写程序来实现它。由于我们在谈论.NET,因此该解决方案仅适用于Windows(XP和更高版本)。 我们将使Web应用程序在angular上。
为什么不选择1和2?
第一项肯定会带来很多痛苦,您必须单独支持浏览器,而浏览器插件则无能为力。 但是,从理论上讲,可以通过插件使用智能卡。 但是,您需要一种更简单的方法。
第二点很容易实现,但是对于这种方案,您不仅必须在站点上而且还要在本地应用程序中进行授权。 这意味着将需要某种界面,但是在更改密码时,还需要在程序中重新授权。 另外,在公司网络中,网络还会存在其他问题,它们通常可以通过具有严格过滤和授权的代理访问Internet,还必须创建一个接口来配置代理,并且无法始终通过自动设置来实现。 对于远离IT部门的用户来说,使用它会更加困难;我们将创建更多的技术支持工作。 当然,您可以为每个用户单独创建一个安装包,以消除对主要授权的需求,但这只会增加问题。
HTTPS与它有什么关系?
当站点通过HTTPS运行时,浏览器将使用HTTP阻止活动内容的下载。 但是,根据事物的逻辑,浏览器应考虑通过HTTP发送到本地计算机的请求是安全的 ,并且不应阻止该请求。 事实并非如此。
下表显示了对Windows平台上的浏览器行为进行的一项小型研究的结果:
下表显示了尝试在适当的地址发出请求时浏览器的行为。 Chromium引擎上的浏览器的行为与Chrome类似,并且Edge 44的行为与IE 11相似。为HTTPS颁发了有效的证书,并使用自签名根证书进行了签名。 https://127.0.0.1和https:// localhost的行为相同,只是127.0.0.1的行为相同,因此您也需要颁发证书,并且很少找到IP地址的证书,因此让我们跳过这一点。
一切都可以在Chrome中运行。 Chrome和IE使用系统证书存储,因此HTTPS也可以在其中使用。 Firefox使用其自己的证书存储,因此它不信任我们的自签名证书。 Firefox和IE不信任本地主机名,这是对的,因为没有人保证它可以解析为127.0.0.1(尽管他们可以像Chrome一样检查它)。
主要问题:IE不允许通过HTTP访问程序。 因此,我们无法避免使用证书。
要使用浏览器,您还需要在程序中指定正确的Access-Control-Allow-Origin,Access-Control-Allow-Methods和Access-Control-Allow-Headers( CORS )标头。
SSL证书
您可以为您的域创建DNS记录,例如local.example.com,它将解析为127.0.0.1。 为该域颁发SSL证书,将其与程序一起分发。 您将必须在程序中分发此证书的私钥。 这是完全不合适的。 并且程序中的证书也将需要更新。
IE将不会信任自签名的SSL证书,它必须使用受信任的根证书进行签名(并且可以是自签名的)。
您可以生成根证书和SSL证书,然后将其与程序一起分发,添加到本地证书存储中。 看起来不安全。 并且可能还需要吊销或更新证书。 因此,我们将在程序首次启动时直接在用户计算机上生成带有密钥的证书。
在C#中创建证书
对于.NET,有一个BouncyCastle库可以满足我们的所有需求。 唯一的问题是,要向存储中添加证书,您将必须请求增加特权。 您还将需要提升的权限才能使用netsh将证书保护到系统中的特定端口。
netsh http add sslcert ipport=0.0.0.0:{PORT} certhash={certThumbprint}
在示例中,SslHelper类中的RegisterSslOnPort方法可以完成此任务。
C#程序中的HTTP服务
要创建轻量级的HTTP(S)服务器,我们使用Nancy库 。 Nancy是用于.NET的轻量级Web框架,简单易用。 关于他的很多文章,包括关于哈布雷的著作。 多亏了Nancy.SelfHosting模块,我们可以在不使用IIS的情况下托管应用程序。
例如,让我们创建一个处理两个数字相加的端点。 在此设置正确的CORS标头很重要,否则浏览器将不会执行对我们API的请求。
南希模块 public class CalcNancyModule : NancyModule { public CalcNancyModule() {
将Nancy初始化添加到我们的应用程序中,我们已经准备好进行战斗。
南希初始化 var hostConfigs = new HostConfiguration(); hostConfigs.UrlReservations.CreateAutomatically = true; hostConfigs.RewriteLocalhost = false; var uris = new Uri[] { new Uri($"http://localhost:{HTTPPORT}"), new Uri($"http://127.0.0.1:{HTTPPORT}"), new Uri($"https://localhost:{HTTPSPORT}") }; using (var host = new NancyHost(hostConfigs, uris)) { host.Start(); }
首先,您需要生成证书并将其放置在商店中,以请求适当的权限。 SslHelper类用于这些操作,其中唯一的公共CheckOrCreateCertificates方法可完成此操作。 作为参数,将SubjectName证书传递给它。 该方法检查必要的证书是否可用,如果没有,系统将创建它们。
为了模拟示例中的辛苦工作和长时间的延迟,请将Thread.Sleep(1000)添加到我们的API调用中。
在此应用程序已准备好运行时,请转到Web。
网络应用
从浏览器行为表中可以看到,一个端点不能被放弃;必须至少使用两个端点:
在Web应用程序中,我们需要确定我们是否在IE(或Edge)中-使用HTTPS,否则请使用-HTTP。 您可以使其更加可靠,而不必找出我们所处的浏览器,而只是尝试执行对我们API的GET / Calc方法的请求,如果请求成功,则我们可以工作,否则,我们可以尝试另一种协议。
仅当Web应用程序本身使用HTTPS时,所有这些都是必要的,因为在使用HTTP协议时,浏览器不会对请求施加限制,只需要正确的CORS标头即可。
在角度应用程序中,创建一个InteractionService服务,该服务将首先通过HTTP,然后通过HTTPS检查本地端点的可用性。 该检查通过checkAvailability方法执行,并且当您使用初始值false订阅了BehaviorSubject类型的$变量时,测试结果可用。
我们将添加数字的工作添加到AppComponent组件中。 当您单击“计算”按钮时,Web应用程序将请求GET / Calc / Add?Num1 = {num1}&num2 = {num2}。 答案或错误显示在结果字段中。
调试时,即使通过HTTPS,您也可能不会注意到问题,因为请求的域将是相同的-本地主机。 因此,您需要使用其他域名测试应用程序。
为了尽可能简化部署Web应用程序的工作,我们使用服务https://stackblitz.com ,这是一个针对角度的Web IDE,不仅具有VSCode的味道。 完成的应用程序可在链接上获得 。
您可以在此处挖掘代码 。
该应用程序无法在stackblitz上交互工作,您需要在单独的私有选项卡中或在其他浏览器中通过https://angular-pfwfrm.stackblitz.io打开它。
怎么尝试?
只需单击链接https://angular-pfwfrm.stackblitz.io ,即可使用stackblitz方便地启动该Web应用程序。
您可以在本地运行Web应用程序。
为此,您需要为此,您需要克隆存储库:
git clone https://github.com/jdtcn/InteractionExample.git cd InteractionExample
在AngularWebApp文件夹中,您需要运行以下命令:
npm install ng serve --ssl true
该Web应用程序将在https://本地主机:4200 /
本地应用程序可以使用Visual Studio从示例(从CsClientApp文件夹打开CsClientApp.sln)进行编译并运行,也可以使用LINQPad程序的脚本。
如果您是.NET开发人员并且不使用LINQPad ,请务必阅读它,这是开发中必不可少的内容。 要运行该示例,您需要在LINQPad'e中打开脚本(第一次需要使用管理员权限运行LINQPad才能安装证书),然后安装Nouget软件包BouncyCastle,Nancy,Nancy.Hosting.Self,然后运行脚本。 之后,您可以单击Web应用程序中的“计算”按钮,并获得操作结果。
安全性
在真实的应用程序中正确形成CORS标头很重要,这样其他站点的恶棍都无法访问我们的程序。 如果小人有机会在其计算机上使用用户的特权并绕过CORS检查,那么他将能够执行我们程序可以做的所有事情。
在任何情况下,该程序都应以最小的权限运行,并且如果它对文档做一些敏感的事情,则需要向其添加请求以确认操作。
结论
看来看似简单的任务非常繁琐,甚至需要附加拐杖来处理证书。
这种方法在实际应用中表现良好。 当然,要使用示例中的代码,您需要添加常规的错误处理。
在安装程序时,请求特权提升很方便,使用InnoSetup可以很容易地做到这一点,并在程序第一次运行时传递必要的属性。 另外,在安装之前,可以方便地检查.NET 4的存在,如果未安装,请进行安装。
Virustotal的人都没有对此程序做出反应,但我想这样做! 但是,如果您在InnoSetup中组装安装程序包,则将开始使用一些三流的防病毒软件。 这有助于摆脱使用代码签名证书进行安装程序签名的麻烦。
程序的自动更新在幕后,但在实际应用中肯定不会多余。 Squirrel非常适合管理自动更新。 删除程序时,使用松鼠从系统中删除我们的证书也很方便。
示例代码发布在GitHub上 。
感谢您的关注!