ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Windows Terminal 连接 Azure Cloud Shell 的完整设计与源码实现

Windows Terminal 连接 Azure Cloud Shell 的完整设计与源码实现 Windows Terminal 连接 Azure Cloud Shell 的完整设计与源码实现【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminalWindows Terminal 允许用户把 Azure Cloud Shell 当作一个连接直接打开本文以规格文档 Azure cloud shell connector 为骨架完整讲清这一特性的设计动机、认证流程、连接生命周期并深入仓库源码逐条印证ITerminalConnection接口是如何被AzureConnection实现的。读完你能掌握终端连接的抽象模型、基于设备码Device Code Flow的无浏览器认证方案以及令牌存储、多租户选择、WebSocket 建立等关键实现细节。![Azure Cloud Shell 在 Windows Terminal 中作为独立配置文件出现的示意图](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_sourcegitcode_repo_files#1235 - Azure cloud shell connector/images/azProf.png)一、设计目标把 Azure 服务带进 Windows Terminal规格文档issue #1235开篇的摘要非常直接这份规格描述了一个功能——让 Windows Terminal 用户连接到 Azure Cloud Shell并包含实现与设计考量。其灵感Inspiration是让开发者在 Windows Terminal 应用内就能顺滑地访问自己的 Azure 服务以方便的方式与 Azure 技术打交道。从仓库的实际落地看这个方便被落实为一项产品能力当平台支持时用户会看到一个名为 Azure Cloud Shell 的动态配置文件见上方配图实际实现中该配置拥有独立图标选中它即可发起一次完整的云端连接。这一能力对应源码中的配置文件生成器 AzureCloudShellGenerator。规格文档同时明确了整个功能必须以隔离方式实现——它对 Windows Terminal 应用本体几乎没有依赖。这样一旦终端支持插件/扩展这个连接器就能直接变成一个插件。这一点在源码中得到忠实贯彻AzureConnection没有侵入终端主流程而是作为一个普通的连接类型存在。二、认证方案设备码流程与 Azure AD v1.0规格文档对认证用户这一步的设计是全文最关键的部分原因有二为什么用设备码流程Device Code Flow因为 Windows Terminal当时不支持拉起浏览器。设备码流程允许用户在一个带屏幕的设备这里就是终端上输入一段短码然后在另一台设备的浏览器里完成登录终端这边通过轮询拿到令牌。为什么用 Azure AD v1.0 而不是 v2.0因为 v2.0即 Microsoft Identity Platform彼时不支持个人账号personal accounts走设备码流程。而 Azure Cloud Shell 用户中个人账号很常见因此选 v1.0。关于令牌的存放规格文档的要求是认证成功后把登录/令牌信息存下来避免用户每次都走一遍设备码流程由于这是敏感信息令牌需加密存储。规格当时指向的是 Windows Storage 加上 Windows Security Data ProtectionDPAPI。这里需要特别指出实现与规格的差异从源码看真正落地采用的是 Windows.Security.Credentials 命名空间下的PasswordVault/PasswordCredential而非规格里设想的 Storage DPAPI 组合。以 AzureConnection.cpp 为例_StoreCredential通过PasswordVault把用户名字段JSON 序列化的租户信息与密码字段JSON 序列化的 accessToken / refreshToken / expiry打包存入资源名固定为Terminal// src/cascadia/TerminalConnection/AzureConnection.cpp void AzureConnection::_StoreCredential() { WDJ::JsonObject userName; userName.SetNamedValue(Lver, WDJ::JsonValue::CreateNumberValue(CurrentCredentialVersion)); _packTenant(userName, *_currentTenant); WDJ::JsonObject passWord; passWord.SetNamedValue(LaccessToken, WDJ::JsonValue::CreateStringValue(_accessToken)); passWord.SetNamedValue(LrefreshToken, WDJ::JsonValue::CreateStringValue(_refreshToken)); passWord.SetNamedValue(Lexpiry, WDJ::JsonValue::CreateStringValue(std::to_wstring(_expiry))); PasswordVault vault; PasswordCredential newCredential{ PasswordVaultResourceName, userName.Stringify(), passWord.Stringify() }; vault.Add(newCredential); }其中CurrentCredentialVersion是一个令牌版本常量当前为 2。源码在_RunAccessState里会读取每条已存凭据的ver字段凡是版本不一致的旧凭据都会被直接移除vault.Remove(entry)只保留最新格式。这是一种平滑的存储格式迁移机制。令牌临近过期时timeNow _expireLimit _expiry其中_expireLimit为 2700 秒会自动走_RefreshTokens刷新并回写。结论规格文档给出了隔离实现 设备码认证 加密令牌存储的设计意图而源码在存储用什么 API这一细节上与原始设想不同但总体方向避免重复登录、安全存储、自动刷新完全一致。三、连接生命周期ITerminalConnection与状态机规格文档中反复强调的核心设计原则是连接器应遵循现有的ITerminalConnection接口使 Azure 只是Windows Terminal 能建立的一种连接类型。这个接口定义在 ITerminalConnection.idl是终端所有后端连接本地 ConPTY、远程、Azure 等的统一抽象interface ITerminalConnection { void Initialize(Windows.Foundation.Collections.ValueSet settings); void Start(); void WriteInput(Char[] data); void Resize(UInt32 rows, UInt32 columns); void Close(); event TerminalOutputHandler TerminalOutput; event Windows.Foundation.TypedEventHandlerITerminalConnection, Object StateChanged; Guid SessionId { get; }; ConnectionState State { get; }; };配套的ConnectionState枚举NotConnected / Connecting / Connected / Closing / Closed / Failed就是连接状态的通用词汇。规格文档所说的以隔离方式实现、将来可变成插件本质就是让AzureConnection实现这五个方法加两个事件从而即插即用地挂进终端的连接体系。AzureConnection 的内部状态机AzureConnection在 AzureConnection.h 里定义了一个更细的业务状态机AzureState用来描述从拿到账号到进入云端终端的完整过程状态含义对应实现方法AccessStored检查是否已有保存的凭据让用户选择复用/新登录/删除_RunAccessStateDeviceFlow无凭据或用户选择新账号走设备码认证_RunDeviceFlowStateTenantChoice账号有多个租户需用户选择_RunTenantChoiceStateStoreTokens询问是否保存本次凭据供下次使用_RunStoreStateTermConnecting已备齐 tenantID / 令牌发起连接_RunConnectStateTermConnected已进入云端终端循环读取 WebSocket在_OutputThread中内联处理驱动这台状态机的是Start()里创建的一条输出线程_OutputThread见 AzureConnection.cpp。线程内是一个switch(_state)大循环每处理完一个状态就推进到下一个直到TermConnected后进入 WebSocket 读取循环。这里体现了规格中认证、申请 cloud shell、申请终端走 HTTP连接终端走 WebSocket的分层设计。几个值得注意的实现事实均可在源码确认ConnectionTypeGUID每个连接类型都有全局唯一标识AzureConnection的是{0xd9fcfdfa, 0xa479, 0x412c, {0x83, 0xb7, 0xc5, 0x64, 0xe, 0x61, 0xcd, 0x62}}。配置文件正是靠这个 GUID 关联到AzureConnection。IsAzureConnectionAvailable()返回AzureClientID ! L0。规格与源码注释都说明客户端 ID 只在正式发布流水线里注入本地构建会得到占位值0因此本地构建会主动禁用 Azure 连接避免连不上还白报错。Initialize解析初始尺寸与会话从settings里读取initialRows / initialCols / sessionId若sessionId为空则用Utils::CreateGuid()生成一个。Resize在已连接时会向{cloudShellUri}terminals/{id}/size?colsrowsversion2019-01-01发请求从而把本地窗口缩放同步到云端终端。四、从规格到落地HTTP WebSocket 的实现规格文档提出前三步认证、请求 cloud shell、请求 terminal用 HTTP最后一步连接终端用 WebSocket并点名了 cpprestsdk 作为 HTTP 客户端库理由同为微软维护出问题便于内部协同。这里再次出现实现与规格的偏差值得如实说明从源码看HTTP 请求用的是 WinRT 的winrt::Windows::Web::Http::HttpClient封装在_SendRequestReturningJson中而 WebSocket 升级用的是 WinHttp 系列 APIWinHttpOpen→WinHttpConnect→WinHttpOpenRequest→WinHttpWebSocketCompleteUpgrade并没有引入 cpprestsdk。也就是说规格里的库选型在落地时被替换成了 Windows 原生的 WinRT / WinHttp 方案。这一点对理解规格文档与最终代码的关系很典型规格记录的是设计阶段的判断代码才是最终事实。关键的连接建立逻辑集中在_RunConnectState与_GetTerminal拉取用户云控制台设置_GetCloudShellUserSettings从properties.preferredShellType解析用户偏好的 shell缺省回退到pwsh_ParsePreferredShellType。申请一个 cloud shell_GetCloudShell向providers/Microsoft.Portal/consoles/default发PUT请求体固定{properties: {osType: linux}}拿到properties.uri作为_cloudShellUri。为该 cloud shell 申请一个 terminal_GetTerminal向{uri}terminals?colsrowsversion2019-01-01shell{shellType}发POST拿到终端id。推导 WebSocket 端点源码对两种形态做了区分处理——若 cloud shell URI 不含servicebus直接把它https换成wss再拼上terminals/{id}若含servicebus则按 cloud shell 团队自己的规则把 socket URI 重排为.../$hc/{ns}/terminals/{id}。注释明确写道这里的逻辑基于 cloud shell 团队自身的方式。升级到 WebSocket后进入TermConnected在输出线程里用WinHttpWebSocketReceive循环读取把 UTF8/BINARY 消息经til::u8u16转码后通过TerminalOutput事件抛给终端 UI。用户输入侧WriteInput在已连接且已进入 TermConnected时直接把数据以WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE发到 WebSocket否则认证阶段会做本地回显、处理退格、按InputMode::Line收集整行输入供认证流程_ReadUserInput读取。五、配置与使用动态配置文件如何出现规格文档在 UI/UX 一节只说会多出一个新配置文件选项实现时会配独立图标。落到仓库里这个多出来的配置文件是由 AzureCloudShellGenerator 作为动态配置文件生成器注入的而非写死在用户 JSON 里// src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp void AzureCloudShellGenerator::GenerateProfiles( std::vectorwinrt::com_ptrimplementation::Profile profiles) const { if (AzureConnection::IsAzureConnectionAvailable()) { auto azureCloudShellProfile{ CreateDynamicProfile(LAzure Cloud Shell) }; azureCloudShellProfile-StartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY }); azureCloudShellProfile-DefaultAppearance().DarkColorSchemeName(LVintage); azureCloudShellProfile-DefaultAppearance().LightColorSchemeName(LVintage); azureCloudShellProfile-ConnectionType(AzureConnection::ConnectionType()); profiles.emplace_back(std::move(azureCloudShellProfile)); } }可以推断出几个使用层面的事实可用性与构建相关IsAzureConnectionAvailable()为假如本地构建时这个配置文件根本不会生成——这也解释了为什么有些 Windows Terminal 安装里看不到 Azure Cloud Shell。外观走 Vintage 配色无论深色还是浅色外观默认都用Vintage配色方案并设置了独立的生成器图标ProfileGeneratorIcons/AzureCloudShell.png。连接类型由 GUID 关联ConnectionType(AzureConnection::ConnectionType())把配置文件绑定到前面那个固定 GUID打开时终端据此实例化AzureConnection。六、能力边界可访问性、安全、可靠性与性能规格文档专设了 Capabilities 一节评估各维度影响这里完整继承其结论可访问性该功能不影响 Windows Terminal 的无障碍能力。安全任何联网功能都引入安全风险文档认为通过正确使用 Azure AD v1.0 并谨慎保管服务端下发的令牌可把风险降到可控范围。从源码看令牌只经PasswordVault这类系统级凭据存储不落明文。可靠性 / 性能 / 功耗 / 效率文档判断这些维度均不受影响。兼容性正因为实现与终端本体基本解耦文档认为不会有既有代码或行为被破坏。七、潜在风险与未来方向规格文档如实列出了三条潜在问题值得读者保留依赖 cpprestsdk 这一开源项目——其代码问题会波及本功能注落地实现已改用 WinRT/WinHttp此条在实现中实际已被规避。Azure AD v1.0 可能被弃用——目前仍受支持但未来有风险文档给出的兜底是最坏情况可切换到 Microsoft Identity Platform只需少量修改 HTTP 请求。Azure Cloud Shell 的 API 并非公开——正式落地需要 Azure Cloud Shell 团队授予应用权限构成一项额外依赖。文档结尾还提出一个展望一旦 Windows Terminal 允许插件/扩展这个 Azure 连接器有可能是终端的第一个插件。这与全文反复出现的隔离实现、可插拔主线呼应也解释了为何源码始终让AzureConnection只通过ITerminalConnection与终端对话。小结本文以 规格文档 为主线还原了认证 → 请求 cloud shell → 请求 terminal → WebSocket 连接的完整链路并逐条对照仓库源码 AzureConnection.cpp / AzureConnection.h / AzureCloudShellGenerator.cpp / ITerminalConnection.idl 印证了实际实现。需要记住的关键差异是令牌存储从规格的 Storage DPAPI 变成了PasswordVaultHTTP/WebSocket 从 cpprestsdk 变成了 WinRT/WinHttp 原生 API——规格记录设计意图代码才是最终事实两者结合阅读才能完整理解这一特性的全貌。【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表