- CMS
- 后端
- Web框架
【免费下载链接】OrchardCore
Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.
导读
本文围绕 OrchardCore 的OrchardCore.Deployment.Remote模块展开,讲解如何将一个 OrchardCore 站点(源站点)上构建的部署计划(Deployment Plan)打包成.zip,通过 HTTPS 推送到另一个 OrchardCore 站点(目标站点)并立即执行其 Recipe,实现内容、配置与文件的跨站点一键迁移。读完本文,你将掌握远程客户端的配置、远程实例的注册、部署计划的远程执行流程、权限模型以及完整的密钥与网络安全加固方案。
模块定位与整体工作流
Remote Deployment 模块为 Deployment 模块 提供了额外的"执行目标"(Deployment Target):把远程 OrchardCore 站点当作部署计划的执行目的地。
从 Manifest.cs 可以看到,该模块声明依赖OrchardCore.Deployment,归类为 "Deployment"。模块描述为 "Provide the ability to export and import to and from a remote server."
一次完整的远程部署遵循如下流程:
- 源站点执行某个部署计划时,由
ExportRemoteInstanceController.Execute调用IDeploymentManager.ExecuteDeploymentPlanAsync生成部署包,并用ZipFile.CreateFromDirectory打包成<计划名>.zip; - 源站点通过
HttpClient以multipart/form-data形式向目标站点的导入端点发送POST请求,表单字段包含Content(zip 文件)、ClientName、ApiKey; - 目标站点的
ImportRemoteInstanceController.Import校验客户端名称与 API Key,将上传的 zip 解压到临时目录,然后调用IDeploymentManager.ImportDeploymentPackageAsync执行包内的Recipe.json; - 目标站点返回
HTTP 200 OK,源站点据此在管理界面弹出"部署执行成功"的通知。
前置条件
在启用远程部署前,需要同时满足以下条件:
- 源站点与目标站点都要启用两个功能:
Deployment(部署)与Remote Deployment(远程部署)。 - 网络连通性:源站点必须能够向目标站点发起 HTTPS 请求。
- 功能对齐:目标站点必须启用导出 Recipe 中每个步骤(Step)所依赖的全部功能,否则导入执行时可能因缺少功能而失败。
- 权限收敛:只将部署相关权限授予可信管理员。
⚠️安全警告:远程部署会在目标站点上无需人工确认地直接执行 Recipe。因此必须使用独立、强度足够的 API Key,且仅通过 HTTPS 传输,并只为可信的源站点配置客户端。
配置目标站点:创建远程客户端(Remote Client)
目标站点通过"远程客户端"来授权接收来自源站点的部署包。配置路径为管理后台工具(Tools) > 部署(Deployments) > 远程客户端(Remote Clients):
- 点击添加远程客户端(Add Remote Client);
- 输入一个唯一的客户端名称(Client Name);
- 生成一个高强度 API Key,妥善保存副本,并填入API Key字段;
- 保存客户端。
源码视角:API Key 的加密存储
在目标站点上,API Key 并不是明文落库的。从 RemoteClientService.cs 可以看到:
CreateRemoteClientAsync使用IDataProtector(保护器名称为"OrchardCore.Deployment",且为ToTimeLimitedDataProtector)对 API Key 进行加密,仅把密文ProtectedApiKey写入RemoteClient文档;RemoteClient模型(见 Models/RemoteClient.cs)只包含Id、ClientName、ProtectedApiKey三个字段,不保留明文。
由于 API Key 由ASP.NET Core Data Protection保护,一旦 Data Protection 密钥丢失或被更换,目标站点将无法再解出旧 API Key。此时需要:
- 在目标站点为该客户端重新生成并保存新的 API Key;
- 在源站点更新对应的远程实例配置,使其与新的客户端名称和 API Key 保持一致。
在导入校验时,ImportRemoteInstanceController.Import(见 ImportRemoteInstanceController.cs)会先按ClientName找到客户端,再用_dataProtector.Unprotect还原明文与请求中的ApiKey比对;客户端不存在返回400 Bad Request("The remote client was not provided"),密钥不匹配同样返回400("The Api Key was not recognized")。
配置源站点:添加远程实例(Remote Instance)
源站点需要把目标站点注册为"远程实例",配置路径为管理后台工具(Tools) > 部署(Deployments) > 远程实例(Remote Instances):
点击添加远程实例(Add Remote Instance);
输入一个描述性名称(Name);
在URL字段填写目标租户的远程导入端点:
https://example.com/OrchardCore.Deployment.Remote/ImportRemoteInstance/Import当目标站点不是默认租户时,必须带上租户 URL 前缀,例如
https://example.com/my-tenant/OrchardCore.Deployment.Remote/ImportRemoteInstance/Import;输入在目标站点上创建的客户端名称与API Key;
保存远程实例。
源码视角:远程实例的持久化
RemoteInstance模型(见 Models/RemoteInstance.cs)包含Id、Name、ClientName、Url、ApiKey五个字段。与目标站点不同,源站点上的远程实例文档以明文保存 API Key——这正是 RemoteInstanceService.cs 通过IDocumentManager<RemoteInstanceList>直接持久化的原因,该服务提供GetOrCreateMutableAsync(加载可写副本)与GetOrCreateImmutableAsync(读取缓存副本)两种读取方式,分别用于管理与共享场景。
⚠️安全警告:源站点的远程实例文档中保存着调用目标站点所需的 API Key。必须严格限制对租户数据库、备份文件以及管理远程实例(Manage remote instances)权限的访问范围。
执行部署计划:远程推送
配置完成后即可发起远程部署:
- 在源站点后台进入工具(Tools) > 部署(Deployments) > 计划(Plans);
- 打开目标部署计划;
- 点击执行(Execute);
- 在目标列表中选择已配置的远程实例。
源码视角:导出与推送的完整调用链
RemoteInstanceDeploymentTargetProvider(见 Services/RemoteInstanceDeploymentTargetProvider.cs)实现了IDeploymentTargetProvider,把每个已配置的远程实例转换为一个名为"Sends the deployment plan to a remote instance."的执行目标,路由指向ExportRemoteInstance控制器的Execute动作。执行时(见 ExportRemoteInstanceController.cs):
- 校验当前用户是否具备
DeploymentPermissions.Export(导出数据)权限,否则返回Forbid(); - 读取
DeploymentPlan与远程实例,若任一不存在则返回404; - 使用
TemporaryFileBuilder+DeploymentPlanResult在临时目录中生成部署文件,并以计划名ToSafeName()命名的.zip打包; - 通过
HttpClientFactory创建的客户端,以MultipartFormDataContent发送三个字段:Content(zip 文件流)、ClientName、ApiKey,POST到远程实例的Url; - 若目标返回
HTTP 200 OK,源站点显示成功通知;否则显示"An error occurred while sending the deployment to the remote instance: \"{ReasonPhrase} ({StatusCode})\""; - 无论成功与否,
finally中都会删除本地临时 zip。
目标端收到请求后(见 ImportRemoteInstanceController.cs):
- 该动作标注了
[IgnoreAntiforgeryToken],因为调用方是外部应用,无法携带有效的防伪令牌,安全完全依赖私有 API Key(源码注释对此有明确说明); - 校验
ClientName与ApiKey通过后,将上传文件经由FileCreationService进行上传校验(校验失败同样返回400); - 将 zip 解压到临时目录,调用
_deploymentManager.ImportDeploymentPackageAsync(new PhysicalFileProvider(tempArchiveFolder))执行包内 Recipe; - 若执行过程抛出
RecipeExecutionException,会把StepResult.Errors中的错误逐条写入通知;其他异常则记录日志并提示"Unexpected error occurred while executing a deployment plan."; finally中清理临时 zip 与解压目录。
Startup.cs(见 Startup.cs)注册了AddHttpClient()、FileCreationService、RemoteInstanceService、RemoteClientService、IDeploymentTargetProvider及权限提供器,是上述调用链得以工作的依赖装配入口。
权限模型
该模块定义了三项权限(见 Permissions.cs):
| 权限 | 用途 |
|---|---|
DeploymentPermissions.ManageRemoteInstances | 管理源站点的远程实例(增删改) |
DeploymentPermissions.ManageRemoteClients | 管理目标站点的远程客户端(增删改) |
DeploymentPermissions.ExportRemoteInstances | 向远程实例导出部署包 |
此外,执行部署计划还要求具备 Deployment 模块的「导出数据(Export Data)」权限(即DeploymentPermissions.Export,在ExportRemoteInstanceController.Execute中直接校验)。默认情况下,管理员(Administrator)角色自动获得以上全部权限。
建议采用分离的角色分配:如果操作人员只需要执行部署、而不应能创建或查看远程凭据,则只授予其「导出数据」与「向远程实例导出」权限,不要授予「管理远程实例 / 管理远程客户端」权限。
安全加固清单
- 全程使用 HTTPS:客户端名称与 API Key 随部署包一起传输,明文 HTTP 会直接泄露凭据。
- 每对站点使用独立 API Key:不同源站点与目标站点的配对应使用不同的密钥,避免单点泄露波及全部通道。
- 定期轮换 API Key:并在此后立刻更新目标站点客户端与源站点远程实例两端的配置;一旦怀疑泄露应立即更换。
- 及时清理不再使用的远程客户端与远程实例:减少攻击面与误操作入口。
- 将导出的部署包视为敏感数据:包内可能包含部署计划所选中的内容、配置和文件。
- 关注目标站点的上传限制与文件校验配置:在传输大体积部署包前,先确认上传大小上限(如 Kestrel 请求体限制、反向代理的
client_max_body_size等)与文件类型校验不会拦截。
部署失败排查
当远程部署失败时,按以下顺序逐项核查:
- 目标 URL 是否正确:是否包含正确的租户前缀与完整端点路径
/OrchardCore.Deployment.Remote/ImportRemoteInstance/Import; - 凭据是否一致:客户端名称与 API Key 在源站点远程实例与目标站点客户端两端是否完全匹配(注意 API Key 区分大小写、避免首尾空格);
- 功能是否齐全:目标站点是否已启用 Recipe 每一步所需的功能;
- 反向代理是否放行:是否允许 multipart
POST请求以及所需大小的请求体; - 查看目标站点应用日志:是否出现 Recipe 执行错误或文件上传校验错误(如
RecipeExecutionException记录的错误明细)。
关联阅读
- 部署计划的基础概念与本地导出:Deployment 模块文档(src/docs/reference/modules/Deployment/README.md)
- 模块声明与依赖关系:Manifest.cs
- 导入端校验与执行实现:ImportRemoteInstanceController.cs
- 导出端打包与推送实现:ExportRemoteInstanceController.cs
- 远程客户端服务与密钥加密存储:RemoteClientService.cs
- 远程实例文档服务:RemoteInstanceService.cs
- 权限定义与默认角色:Permissions.cs
- 依赖注入装配:Startup.cs
- CMS
- 后端
- Web框架
【免费下载链接】OrchardCore
Orchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.
相关推荐
Chevrotain完全指南:JavaScript解析器构建工具包的终极入门教程
Chevrotain完全指南:JavaScript解析器构建工具包的终极入门教程 Chevrotain是一个 超快速 且 功能丰富 的JavaScript解析器
StyLua:终极Lua代码格式化工具,一站式解决多版本兼容难题
StyLua:终极Lua代码格式化工具,一站式解决多版本兼容难题 StyLua是一款专为Lua开发者打造的代码格式化工具,能够自动调整代码风格,确保团队协作中的
开发工具CLI代码格式化如何把 MCP Server 部署到 Vercel 与 Cloudflare:完整远程部署指南
如何把 MCP Server 部署到 Vercel 与 Cloudflare:完整远程部署指南 如果你是第一次接触 MCP Server 远程部署 ,这篇指南就
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考