- 桌面应用
- 跨平台
【免费下载链接】Electron.NET
:electron: Build cross platform desktop apps with ASP.NET Core (Razor Pages, MVC, Blazor).
导读
Electron.NET 默认会在 .NET 与 Node.js(Electron 主进程)之间的 IPC 通道上启用启动期安全机制,使同一台机器上不同用户账号运行的多个实例可以安全共存。但对于承载在 ASP.NET Core 之上的 Web 应用本身,这种保护并不充分。本文围绕 docs/Using/Secure-Communication.md 的官方指引,结合ElectronNET.AspNet源码,完整讲解如何通过IElectronAuthenticationService+ElectronAuthenticationMiddleware为 ASP.NET Core 应用开启二次认证,确保只有由当前 .NET 实例派生的 Electron 客户端才能访问应用。读完本文,你将掌握安全通信的完整配置步骤、底层 token 协商链路以及中间件的实际请求校验流程。
默认的 IPC 安全机制:它能保护什么
Electron.NET 的架构中,.NET 进程与 Electron(Node.js)进程之间通过 Socket.IO 桥接通信(对应 SocketBridgeService)。在启动阶段,两者会协商出一个认证令牌(auth token),后续每一次桥接调用都要携带该令牌。
从源码可以还原这条默认保护链路的完整形态:
- 令牌生成与传递:Electron 主进程被 .NET 进程拉起后,通过标准输出回传端口、主机与令牌(见 ElectronProcessActive.cs),并被写入 ElectronNetRuntime.ElectronAuthToken。
- 命令行参数:令牌也可以作为
electronAuthToken参数传给 .NET 侧,由 StartupManager.cs 解析并写入ElectronNetRuntime.ElectronAuthToken;对应的参数常量定义在 ElectronNetRuntime.cs。 - 桥接校验:两种启动模式下(.NET 先启动 / Electron 先启动),运行时控制器都会取出该令牌创建带认证的
SocketBridgeService(见 RuntimeControllerDotNetFirst.cs 与 RuntimeControllerElectronFirst.cs)。
由此,"默认安全"意味着:IPC 桥接层面上,不同用户账号在同一台机器上运行的多个 Electron.NET 实例互不可见、互不干扰。
但正如官方文档明确指出:这不足以保护背后的 Web 应用,也不足以对抗恶意的 root 用户。因为:
- ASP.NET Core 承载的 Web 服务监听在 localhost 端口上,任何能访问该端口的本机进程都可能直接发起 HTTP 请求;
- root 用户拥有系统的最高权限,可以读取进程参数、内存甚至直接伪造请求,单纯依赖进程级隔离无法防护。
为 Web 应用开启认证:两步 opt-in 配置
官方给出的方案是:复用 IPC 桥接已经协商好的认证令牌,让 ASP.NET Core 应用也能验证"发起请求的客户端是否正是当前实例派生的 Electron 进程"。你只需完成两个动作。
第一步:在AddElectron之前注册认证服务
文档强调,关键点在于必须在调用AddElectron之前将认证服务注册到服务容器。这是因为 Electron.NET 在运行时解析IElectronAuthenticationService,如果发现它已被注册,就会在启动阶段把协商好的令牌写入该服务,供后续请求校验使用。
using ElectronNET.AspNet.Middleware; using ElectronNET.AspNet.Services; var builder = WebApplication.CreateBuilder(args); // 必须在 AddElectron 之前注册 builder.Services.AddSingleton<IElectronAuthenticationService, ElectronAuthenticationService>(); builder.Services.AddElectron();注册之后,Electron.NET 就"知道"你希望存储并复用 .NET 与 Node.js 在启动时协商出的认证令牌。这一点可以从 RuntimeControllerAspNetBase.cs 得到印证:当 Electron 进程就绪且 AspNet 生命周期适配器就绪时,运行时会取出ElectronNetRuntime.ElectronAuthToken,并通过this.authenticationService?.SetExpectedToken(token)写入认证服务(注释明确写道"Only if somebody registered an IElectronAuthenticationService service - otherwise we do not care",即只有注册了该服务才会执行)。
第二步:注册认证中间件
接下来注册中间件,对来源非本 Electron.NET 应用的请求直接拒绝:
var app = builder.Build(); // 必须放在所有路由之前 app.UseMiddleware<ElectronAuthenticationMiddleware>(); app.UseRouting();官方文档特别强调:必须置于任何路由之上(例如在UseRouting之前),否则中间件不会对所有请求生效,部分端点可能绕过认证。
源码级原理剖析:令牌如何存储与校验
认证服务:常量时间比较防时序攻击
ElectronAuthenticationService 是IElectronAuthenticationService的默认实现,内部维护一个_expectedToken字段,通过lock保证多线程读写安全,并提供两个核心方法:
SetExpectedToken(string token):由运行时在启动阶段调用,写入期望令牌;ValidateToken(string token):校验传入令牌,先做空值检查,再做常量时间比较(ConstantTimeEquals,按位异或累加),防止通过响应时间差异进行时序攻击(timing attack)。
private static bool ConstantTimeEquals(string a, string b) { if (a == null || b == null || a.Length != b.Length) return false; var result = 0; for (int i = 0; i < a.Length; i++) { result |= a[i] ^ b[i]; } return result == 0; }接口定义见 IElectronAuthenticationService.cs,注释同样明确了其用途:"ensure only the Electron process spawned by this .NET instance can connect"(确保只有本 .NET 实例派生的 Electron 进程能够连接)。
认证中间件:cookie + 查询参数双通道校验
ElectronAuthenticationMiddleware 实现了完整的请求校验逻辑,其安全模型为:
- 首次请求:Electron 主进程加载本地 URL 时会把令牌作为查询参数附加(
?token=guid); - 校验通过后:中间件把令牌写入
HttpOnlyCookie,后续请求不再出现在 URL 中; - 后续请求:凭 Cookie 完成认证。
具体判定流程(对应InvokeAsync):
- 已带 Cookie:取出名为
ElectronAuth的 Cookie 交给ValidateToken校验;有效则放行,无效则返回401 Unauthorized: Invalid authentication; - 未带 Cookie 但带 token 查询参数:校验通过后写入 Cookie(
HttpOnly = true防 XSS 读取、SameSite = SameSiteMode.Strict防 CSRF、Path = "/"全局生效、Secure = false因 localhost 走 HTTP、IsEssential = true标记为必需 Cookie),然后放行;校验失败返回 401; - 两者皆无:记录告警日志并返回
401 Unauthorized: Authentication required。
context.Response.Cookies.Append(AuthCookieName, token, new CookieOptions { HttpOnly = true, // 防止 JavaScript 访问(XSS 防护) SameSite = SameSiteMode.Strict, // CSRF 防护 Path = "/", // 对所有路由有效 Secure = false, // localhost 为 HTTP,故置 false IsEssential = true // 应用正常运行所必需 });令牌如何到达浏览器:Host 侧自动附加
.NET 侧完成令牌校验配置后,Electron 主进程侧(ElectronNET.Host)会在创建窗口加载本地 URL 时自动把令牌附加到查询参数中,见 browserWindows.ts:当loadUrl存在且global["authToken"]可用时,会判断目标是否为 localhost/127.0.0.1/::1,若是则通过url.searchParams.set("token", token)注入令牌。这一设计保证了"第一次请求由 Electron 自己发起时必然携带合法令牌",从而让中间件能够完成首访握手。
完整可运行示例:Blazor + SignalR 模式
仓库内置了专门演示该特性的示例项目 ElectronNET.Samples.AuthMiddleware,其 Program.cs 给出了完整接线顺序,可作为直接参照:
using ElectronNET.API; using ElectronNET.API.Entities; using ElectronNET.AspNet.Middleware; using ElectronNET.AspNet.Services; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); // CORS(SignalR 需要) builder.Services.AddCors(options => { options.AddPolicy("ElectronPolicy", policy => { policy.AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); }); // 1) 先注册认证服务(单例) builder.Services.AddSingleton<IElectronAuthenticationService, ElectronAuthenticationService>(); // 2) 再启用 Electron builder.Services.AddElectron(); builder.WebHost.UseElectron(args, async () => { var options = new BrowserWindowOptions { Show = false, Width = 1200, Height = 800, IsRunningBlazor = true, }; if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux()) options.AutoHideMenuBar = true; var browserWindow = await Electron.WindowManager.CreateWindowAsync(options); browserWindow.OnReadyToShow += () => browserWindow.Show(); }); var app = builder.Build(); // 3) 中间件必须最先注册(先于路由、静态文件等) app.UseMiddleware<ElectronAuthenticationMiddleware>(); app.UseRouting(); app.UseCors("ElectronPolicy"); if (!app.Environment.IsDevelopment()) app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseStaticFiles(); app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true); app.UseAntiforgery(); app.MapStaticAssets(); app.MapRazorComponents<ElectronNET.Samples.AuthMiddleware.Components.App>() .AddInteractiveServerRenderMode(); app.Run();该示例也给出了中间件顺序的最佳实践:认证中间件紧跟在builder.Build()之后、UseRouting之前注册,从而覆盖包括静态文件、Razor 组件端点在内的全部请求。
配置要点与边界说明
顺序约束
- 服务注册:
AddSingleton<IElectronAuthenticationService, ElectronAuthenticationService>()必须出现在AddElectron()之前。只有先注册,运行时才会检测到并调用SetExpectedToken注入令牌。 - 中间件注册:
UseMiddleware<ElectronAuthenticationMiddleware>()必须位于UseRouting之前,确保认证先于路由匹配执行,避免端点绕过校验。
覆盖范围与限制
- 该机制面向Electron.NET 启动模式下由自身派生的 Electron 客户端:令牌由本实例在启动时协商,因此其他实例、浏览器直接访问 localhost 端口时缺少合法令牌,会被中间件以 401 拒绝;
- 文档明确指出,该方案不构成对恶意 root 用户的防护——root 可读取令牌或 Cookie,属于超出应用层认证的能力边界;
- Cookie 的
Secure = false是特意为 localhost HTTP 场景设置的(见源码注释 "False because localhost is HTTP");若将应用部署到 HTTPS 环境,应相应调整; - 若要验证效果,可尝试在 Electron 窗口之外用普通浏览器直接访问应用地址,观察返回
401而非正常页面,同时服务端日志会记录Authentication failed类别的 Warning。
参考阅读
- 官方安全指南原文:docs/Using/Secure-Communication.md
- 认证服务实现:ElectronAuthenticationService.cs
- 认证服务接口:IElectronAuthenticationService.cs
- 认证中间件实现:ElectronAuthenticationMiddleware.cs
- 完整示例项目:ElectronNET.Samples.AuthMiddleware
- 令牌协商与运行时存储:ElectronNetRuntime.cs 与 StartupManager.cs
- Electron 侧令牌注入逻辑:browserWindows.ts
- 桌面应用
- 跨平台
【免费下载链接】Electron.NET
:electron: Build cross platform desktop apps with ASP.NET Core (Razor Pages, MVC, Blazor).
相关推荐
如何掌握Rust宏开发:gh_mirrors/ca/case-studies完全指南
如何掌握Rust宏开发:gh_mirrors/ca/case studies完全指南 Rust宏是元编程的强大工具,能够在编译时生成代码,帮助开发者编写更简洁、
桌面应用跨平台Electron.NET AutoUpdater 实战指南:为 ASP.NET Core 桌面应用实现全平台自动更新
Electron.NET AutoUpdater 实战指南:为 ASP.NET Core 桌面应用实现全平台自动更新 本篇指南完整讲解 Electron.NET
桌面应用跨平台终极指南:如何在ASP.NET Core中使用JWT实现安全认证授权
终极指南:如何在ASP.NET Core中使用JWT实现安全认证授权 在当今Web应用开发中,确保API安全是至关重要的一环。ASP.NET Core作为微软推
文档教程知识库示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考