ASP.NET Core 模块和 IIS 的高级配置

本文介绍 ASP.NET Core 模块和 IIS 的高级配置选项和方案。

修改堆栈大小

仅适用于使用进程内托管模型的情况。

使用 web.config 文件中的 stackSize 设置(以字节为单位)配置托管堆栈大小。 默认大小为 1,048,576 字节 (1 MB)。 下面的示例将堆栈大小更改为 2 MB(2,097,152 字节):

<aspNetCore processPath="dotnet"
    arguments=".\MyApp.dll"
    stdoutLogEnabled="false"
    stdoutLogFile="\\?\%home%\LogFiles\stdout"
    hostingModel="inprocess">
  <handlerSettings>
    <handlerSetting name="stackSize" value="2097152" />
  </handlerSettings>
</aspNetCore>

禁止在配置上轮换

disallowRotationOnConfigChange 设置适用于蓝/绿场景,其中对全局配置的更改不应导致所有站点都回收。 如果此标志为 true,则只有与站点本身相关的更改才会导致其回收。 例如,如果站点的 web.config 更改,或者从 IIS 的角度来看出现了与站点路径相关的更改,站点便会回收。 但对 applicationHost.config 的一般更改并不会导致应用回收。 以下示例将此设置设为 true:

<aspNetCore processPath="dotnet"
    arguments=".\MyApp.dll"
    stdoutLogEnabled="false"
    stdoutLogFile="\\?\%home%\LogFiles\stdout"
    hostingModel="inprocess">
  <handlerSettings>
    <handlerSetting name="disallowRotationOnConfigChange" value="true" />
  </handlerSettings>
</aspNetCore>

此设置对应于 API ApplicationPoolRecycling.DisallowRotationOnConfigChange

代理配置使用 HTTP 协议和配对令牌

仅适用于进程外托管。

在 ASP.NET Core 模块和 Kestrel 之间创建的代理使用 HTTP 协议。 因此,不存在从脱离服务器的位置窃取模块和 Kestrel 之间的流量的风险。

配对令牌用于保证 Kestrel 收到的请求已由 IIS 代理且不来自某些其他源。 模块已创建配对令牌并将其设置到环境变量 (ASPNETCORE_TOKEN)。 此外,配对令牌还设置到每个代理请求的标头 (MS-ASPNETCORE-TOKEN)。 IIS 中间件检查它所接收的每个请求,以确认配对令牌标头值与环境变量值相匹配。 如果令牌值不匹配,则将记录请求并拒绝该请求。 无法从脱离服务器的位置访问配对令牌环境变量及模块和 Kestrel 之间的流量。 如果不知道配对令牌值,攻击者就无法提交绕过 IIS 中间件中的检查的请求。

具有 IIS 共享配置的 ASP.NET Core 模块

ASP.NET Core 模块安装程序使用 TrustedInstaller 帐户的权限运行。 由于本地系统帐户没有 IIS 共享配置所用的共享路径的修改权限,因此在尝试配置共享上的 applicationHost.config 文件中的模块设置时,安装程序将引发拒绝访问错误。

在与 IIS 安装相同的计算机上使用 IIS 共享配置时,请运行 ASP.NET Core Hosting Bundle 安装程序,并将 OPT_NO_SHARED_CONFIG_CHECK 参数设置为 1

dotnet-hosting-{VERSION}.exe OPT_NO_SHARED_CONFIG_CHECK=1

如果共享配置的路径与 IIS 安装的路径不在同一台计算机上,请按照下列步骤操作:

  1. 禁用 IIS 共享配置。
  2. 运行安装程序。
  3. 将已更新的 applicationHost.config 文件导出到文件共享。
  4. 重新启用 IIS 共享配置。

数据保护

ASP.NET Core 数据保护堆栈由多个 ASP.NET Core 中间件使用,包括用于身份验证的中间件。 即使用户代码不调用数据保护 API,也应该使用部署脚本或在用户代码中配置数据保护,以创建持久的加密密钥存储。 如果不配置数据保护,则密钥存储在内存中。重启应用时,密钥会被丢弃。

如果应用重启时数据保护密钥环存储于内存中:

  • 所有基于 cookie 的身份验证令牌都无效。
  • 用户需要在下一次请求时再次登录。
  • 无法再解密使用密钥环保护的任何数据。 这可能包括 CSRF 令牌ASP.NET Core MVC TempData cookie

若要在 IIS 下配置数据保护以持久化密钥环,请使用以下方法之一:

  • 创建数据保护注册表项

    ASP.NET Core 应用使用的数据保护密钥存储在应用外部的注册表中。 要持久保存给定应用的密钥,需为应用池创建注册表项。

    对于独立的非 Web 场 IIS 安装,可以对用于 ASP.NET Core 应用的每个应用池使用数据保护 Provision-AutoGenKeys.ps1 PowerShell 脚本。 此脚本在 HKLM 注册表中创建注册表项,仅应用的应用池工作进程帐户可对其进行访问。 通过计算机范围的密钥使用 DPAPI 对密钥静态加密。

    在 Web 场方案中,可以将应用配置为使用 UNC 路径存储其数据保护密钥环。 默认情况下,密钥未加密。 确保网络共享的文件权限仅限于应用在其下运行的 Windows 帐户。 可使用 X509 证书来保护静态密钥。 考虑允许用户上传证书的机制。 将证书置于用户信任的证书存储中,并确保这些证书对所有运行用户应用的计算机都可用。 有关详细信息,请参阅配置 ASP.NET Core 数据保护

  • 配置 IIS 应用程序池以加载用户配置文件

    此设置位于应用池“高级设置”下的“进程模型”部分 。 将“加载用户配置文件”设置为 True。 如果设置为 True,会将密钥存储在用户配置文件目录中,并使用 DPAPI 和特定于用户帐户的密钥进行保护。 密钥保留在 %LOCALAPPDATA%/ASP.NET/DataProtection-Keys 文件夹中。

    同时还必须启用应用池的 setProfileEnvironment 属性setProfileEnvironment 的默认值为 true。 在某些情况下(例如,Windows 操作系统),将 setProfileEnvironment 设置为 false。 如果密钥未按预期存储在用户配置文件目录中,请执行以下操作:

    1. 导航到 %windir%/system32/inetsrv/config 文件夹。
    2. 打开 applicationHost.config 文件。
    3. 查找 <system.applicationHost><applicationPools><applicationPoolDefaults><processModel> 元素。
    4. 确认 setProfileEnvironment 属性不存在,这会将值默认设置为 true,或者将属性的值显式设置为 true
  • 将文件系统用作密钥环存储

    调整应用代码,将文件系统用作密钥环存储。 使用 X509 证书保护密钥环,并确保该证书是受信任的证书。 如果它是自签名证书,则将该证书放置于受信任的根存储中。

    当在 Web 场中使用 IIS 时:

  • 设置用于数据保护的计算机范围的策略

    数据保护系统对以下操作提供有限支持:为使用数据保护 API 的所有应用设置默认计算机范围的策略。 有关详细信息,请参阅 ASP.NET Core 数据保护概述

IIS 配置

Windows Server 操作系统

启用 Web 服务器 (IIS) 服务器角色并建立角色服务。

  1. 通过“管理”菜单或“服务器管理器”中的链接使用“添加角色和功能”向导。 在“服务器角色”步骤中,选中“Web 服务器(IIS)”框 。

    The Web Server IIS role is selected in the Select server roles step.

  2. 在“功能”步骤后,为 Web 服务器 (IIS) 加载“角色服务”步骤。 选择所需 IIS 角色服务,或接受提供的默认角色服务。

    The default role services are selected in the Select role services step.

    Windows 身份验证(可选)
    若要启用 Windows 身份验证,请依次展开以下节点:“Web 服务器”>“安全”。 选择“Windows 身份验证”功能。 有关详细信息,请参阅 Windows 身份验证 <windowsAuthentication>配置 Windows 身份验证

    Websocket(可选)
    Websocket 支持 ASP.NET Core 1.1 或更高版本。 若要启用 WebSocket,请依次展开以下节点:“Web 服务器”>“应用开发”。 选择“WebSocket 协议”功能。 有关详细信息,请参阅 WebSockets

  3. 继续执行“确认”步骤,安装 Web 服务器角色和服务。 安装 Web 服务器 (IIS) 角色后无需重启服务器/IIS。

Windows 桌面操作系统

启用“IIS 管理控制台”和“万维网服务”。

  1. 导航到“控制面板”>“程序”>“程序和功能”>“启用或禁用 Windows 功能”(位于屏幕左侧) 。

  2. 打开“Internet Information Services”节点。 打开“Web 管理工具”节点。

  3. 选中“IIS 管理控制台”框。

  4. 选中“万维网服务”框。

  5. 接受“万维网服务”的默认功能,或自定义 IIS 功能。

    Windows 身份验证(可选)
    若要启用 Windows 身份验证,请依次展开以下节点:“万维网服务”>“安全”。 选择“Windows 身份验证”功能。 有关详细信息,请参阅 Windows 身份验证 <windowsAuthentication>配置 Windows 身份验证

    Websocket(可选)
    Websocket 支持 ASP.NET Core 1.1 或更高版本。 若要启用 WebSocket,请依次展开以下节点:“万维网服务”>“应用开发功能”。 选择“WebSocket 协议”功能。 有关详细信息,请参阅 WebSockets

  6. 如果 IIS 安装需要重新启动,则重新启动系统。

IIS Management Console and World Wide Web Services are selected in Windows Features.

虚拟目录

ASP.NET Core 应用不支持 IIS 虚拟目录。 可将应用托管为子应用程序

子应用程序

可将 ASP.NET Core 应用托管为 IIS 子应用程序(子应用)。 子应用的路径成为根应用 URL 的一部分。

在 MVC 和 Razor Pages 中,子应用内的静态资产链接应使用波形符-斜杠 (~/) 表示法。 波形符-斜杠符号触发标记帮助器,来将子应用的基路径追加到呈现的相关链接前面。 对于 /subapp_path 处的子应用,使用 src="~/image.png" 链接的图像将呈现为 src="/subapp_path/image.png"。 根应用的静态文件中间件不处理静态文件请求。 此请求由子应用的静态文件中间件处理。

注意

Razor 组件 (.razor) 不得使用波形符-斜杠表示法。 有关详细信息,请参阅Blazor 应用基路径文档

若将静态资产的 src 属性设置为绝对路径(如 src="/image.png"),则呈现的链接不包含子应用的基路径。 根应用的静态文件中间件试图从根应用的 web 根目录提供资产,这会导致“404 - 找不到”响应生成,除非可以从根应用获得静态资产。

若要将 ASP.NET Core 应用作为子应用托管在其他 ASP.NET Core 应用下:

  1. 为此子应用创建应用池。 将“.NET CLR 版本”设置为“无托管代码”,因为将启动 .NET Core 的核心公共语言运行时 (CoreCLR) ,将应用托管在工作进程中,而非桌面 CLR (.NET CLR) 中 。

  2. 在 IIS 管理器中添加根网站,并且此子应用在根网站的某个文件夹中。

  3. 在 IIS 管理器中右击此子应用文件夹,并选择“转换为应用程序”。

  4. 在“添加应用程序”对话框中,使用“应用程序池”的“选择”按钮来分配为子应用创建的应用池。 选择“确定”。

使用进程内托管模型时,需要向子应用分配单独的应用池。

有关进程内托管模型及 ASP.NET Core 模块配置的详细信息,请参阅用于 IIS 的 ASP.NET Core 模块 (ANCM)

应用程序池

由托管模型决定应用池隔离:

  • 托管在进程内:应用需要在单独的应用池中运行。
  • 托管在进程外:建议在每个应用自己的应用池中运行各应用,以彼此隔离。

IIS“添加网站”对话框默认为每应用一个应用池。 提供了站点名称时,该文本会自动传输到“应用程序池”文本框 。 添加站点时,会使用该站点名称创建新的应用池。

应用程序池 Identity

通过应用池标识帐户,可以在唯一帐户下运行应用,而无需创建和管理域或本地帐户。 在 IIS 8.0 或更高版本上,IIS 管理员工作进程 (WAS) 将使用新应用池的名称创建一个虚拟帐户,并默认在此帐户下运行应用池的工作进程。 在 IIS 管理控制台中,确保应用池“高级设置”下的“”设置为使用 ApplicationPoolIdentity

Application pool advanced settings dialog

IIS 管理进程使用 Windows 安全系统中应用池的名称创建安全标识符。 可使用此标识保护资源。 但是,此标识不是真实的用户帐户,不会在 Windows 用户管理控制台中显示。

如果 IIS 工作进程需要对应用的高级访问权限,请为包含该应用的目录修改访问控制列表 (ACL):

  1. 打开 Windows 资源管理器并导航到目录。

  2. 右键单击该目录,然后选择“属性”。

  3. 在“安全”选项卡下,选择“编辑”按钮,然后单击“添加”按钮 。

  4. 选择“位置”按钮,并确保该系统处于选中状态。

  5. 在“输入要选择的对象名”区域中,输入 IIS AppPool\{APP POOL NAME} 格式,其中 {APP POOL NAME} 占位符为应用池名称。 选择“检查名称”按钮。 对于 DefaultAppPool,请使用 IIS AppPool\DefaultAppPool 检查名称。 如果选择了“检查名称”按钮,对象名称区域中将指示 DefaultAppPool。 无法直接在对象名称区域中输入应用池名称。 检查对象名称时使用 IIS AppPool\{APP POOL NAME} 格式,其中 {APP POOL NAME} 占位符是应用池名称。

    Select users or groups dialog for the app folder: The app pool name of

  6. 选择“确定”

    Select users or groups dialog for the app folder: After selecting

  7. 默认情况下,应授予读取和执行权限。 根据需要请提供其他权限。

也可使用 ICACLS 工具在命令提示符处授予访问权限。 以 DefaultAppPool 为例,以下命令用于授予对 MyWebApp 文件夹、子文件夹和文件的读取和执行权限:

ICACLS C:\sites\MyWebApp /grant "IIS AppPool\DefaultAppPool:(OI)(CI)RX"

有关详细信息,请参阅 icacls 主题。

HTTP/2 支持

以下 IIS 部署方案中的 ASP.NET Core 支持 HTTP/2

  • 进程内
    • Windows Server 2016/Windows 10 或更高版本;IIS 10 或更高版本
    • TLS 1.2 或更高版本的连接
  • 进程外
    • Windows Server 2016/Windows 10 或更高版本;IIS 10 或更高版本
    • 面向公众的边缘服务器连接使用 HTTP/2,但与 Kestrel 服务器的反向代理连接使用 HTTP/1.1。
    • TLS 1.2 或更高版本的连接

对于建立了 HTTP/2 连接时的进程内部署,HttpRequest.Protocol 报告 HTTP/2。 对于建立了 HTTP/2 连接时的进程外部署,HttpRequest.Protocol 报告 HTTP/1.1

有关进程内和进程外托管模型的详细信息,请参阅用于 IIS 的 ASP.NET Core 模块 (ANCM)

默认情况下将启用 HTTP/2。 如果未建立 HTTP/2 连接,连接会回退到 HTTP/1.1。 有关使用 IIS 部署的 HTTP/2 配置的详细信息,请参阅 IIS 上的 HTTP/2

CORS 预检请求

本部分仅适用于面向 .NET Framework 的 ASP.NET Core 应用程序。

对于面向 .NET Framework 的 ASP.NET Core 应用程序,默认情况下,IIS 不会将 OPTIONS 请求传递给应用程序。 若要了解如何在 web.config 中配置应用的 IIS 处理程序以传递 OPTIONS 请求,请参阅在 ASP.NET Web API 2 中启用跨域请求:CORS 的工作原理

应用程序初始化模块和空闲超时

通过 ASP.NET Core 模板版本 2 托管在 IIS 中时:

应用程序初始化模块

适用于托管在进程内和进程外的应用。

IIS 应用程序初始化是一种 IIS 功能,可在应用池启动或回收时向应用发送 HTTP 请求。 该请求会触发应用启动。 默认情况下,IIS 向应用的根 URL (/) 发出请求以初始化应用(有关配置的详细信息,请参阅更多资源)。

确认已启用 IIS 应用程序初始化角色功能:

Windows 7 或更高版本桌面系统,在本地使用 IIS 时:

  1. 导航到“控制面板”>“程序”>“程序和功能”>“启用或禁用 Windows 功能”(位于屏幕左侧) 。
  2. 打开“Internet Information Services”>“万维网服务”>“应用程序开发功能”。
  3. 选中“应用程序初始化”的复选框。

Windows Server 2008 R2 或更高版本:

  1. 打开“添加角色和功能向导”。
  2. 在“选择角色服务”面板中,打开“应用程序开发”节点 。
  3. 选中“应用程序初始化”的复选框。

使用下面的任一方法为站点启用“应用程序初始化模块”:

  • 使用 IIS 管理器:

    1. 在“连接”面板中选择“应用程序池”。
    2. 在列表中右键单击应用的应用池,并选择“高级设置”。
    3. 默认的“启动模式”为“”。 将“启动模式”设置为“”。 选择“确定”。
    4. 打开“连接”面板中的“网站”节点。
    5. 右键单击应用,并选择“管理网站”>“高级设置” 。
    6. 默认的“预加载已启用”设置为“”。 将“预加载已启用”设置为“”。 选择“确定” 。
  • 使用 web.config 向应用的 web.config 文件中的 <system.webServer> 元素添加 <applicationInitialization> 元素(其中 doAppInitAfterRestart 设置为 true):

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <location path="." inheritInChildApplications="false">
        <system.webServer>
          <applicationInitialization doAppInitAfterRestart="true" />
        </system.webServer>
      </location>
    </configuration>
    

空闲超时

仅适用于托管在进程内的应用。

若要防止应用进入空闲状态,请使用 IIS 管理器设置应用池的空闲超时:

  1. 在“连接”面板中选择“应用程序池”。
  2. 在列表中右键单击应用的应用池,并选择“高级设置”。
  3. 默认的“空闲超时(分钟)”为“”分钟。 将“空闲超时(分钟)”设置为“”(零)。 选择“确定”。
  4. 回收工作进程。

若要防止托管在进程外的应用超时,请使用下列任一方法:

关于应用程序初始化模块和空闲超时的更多资源

模块、架构和配置文件位置

模块

IIS (x86/amd64)

  • %windir%\System32\inetsrv\aspnetcore.dll

  • %windir%\SysWOW64\inetsrv\aspnetcore.dll

  • %ProgramFiles%\IIS\Asp.Net Core Module\V2\aspnetcorev2.dll

  • %ProgramFiles(x86)%\IIS\Asp.Net Core Module\V2\aspnetcorev2.dll

IIS Express (x86/amd64)

  • %ProgramFiles%\IIS Express\aspnetcore.dll

  • %ProgramFiles(x86)%\IIS Express\aspnetcore.dll

  • %ProgramFiles%\IIS Express\Asp.Net Core Module\V2\aspnetcorev2.dll

  • %ProgramFiles(x86)%\IIS Express\Asp.Net Core Module\V2\aspnetcorev2.dll

架构

IIS

  • %windir%\System32\inetsrv\config\schema\aspnetcore_schema.xml

  • %windir%\System32\inetsrv\config\schema\aspnetcore_schema_v2.xml

IIS Express

  • %ProgramFiles%\IIS Express\config\schema\aspnetcore_schema.xml

  • %ProgramFiles%\IIS Express\config\schema\aspnetcore_schema_v2.xml

Configuration

IIS

  • %windir%\System32\inetsrv\config\applicationHost.config

IIS Express

  • Visual Studio:{APPLICATION ROOT}\.vs\config\applicationHost.config

  • iisexpress.exe CLI:%USERPROFILE%\Documents\IISExpress\config\applicationhost.config

通过在 applicationHost.config 文件中搜索 aspnetcore 可以找到这些文件。

使用 Visual Studio 进行发布时安装 Web 部署

使用 Web 部署将应用部署到服务器时,请在服务器上安装最新版本的 Web 部署。 若要安装 Web 部署,请从 Microsoft 下载中心获取安装程序。

创建 IIS 站点

  1. 在托管系统上,创建一个文件夹以包含应用已发布的文件夹和文件。 在接下来的步骤中,文件夹路径作为应用程序的物理路径提供给 IIS。 有关应用部署文件夹和文件布局的详细信息,请参阅 ASP.NET Core 目录结构

  2. 在 IIS 管理器中,打开“连接”面板中的服务器节点。 右键单击“站点”文件夹。 选择上下文菜单中的“添加网站”。

  3. 提供网站名称,并将物理路径设置为应用的部署文件夹 。 提供“绑定”配置,并通过选择“确定”创建网站:

    Supply the Site name, physical path, and Host name in the Add Website step.

    警告

    不应使用顶级通配符绑定(http://*:80/http://+:80)。 顶级通配符绑定可能会为应用带来安全漏洞。 此行为同时适用于强通配符和弱通配符。 使用显式主机名而不是通配符。 如果可控制整个父域(区别于易受攻击的 *.com),则子域通配符绑定(例如,*.mysub.com)不具有此安全风险。 有关详细信息,请参阅 RFC 9110:HTTP 语义(第 7.2 节:托管和授权)

  4. 在服务器节点下,选择“应用程序池”。

  5. 右键单击站点的应用池,然后从上下文菜单中选择“基本设置”。

  6. 在“编辑应用程序池”窗口中,将“.NET CLR 版本”设置为“无托管代码”:

    Set No Managed Code for the .NET CLR version.

    ASP.NET Core 在单独的进程中运行,并管理运行时。 ASP.NET Core 不依赖桌面 CLR (.NET CLR) 加载。 将启动 .NET Core 的 Core 公共语言运行时 (CoreCLR),在工作进程中托管应用。 将“.NET CLR 版本”设置为“无托管代码”是可选步骤,但建议采用此设置。

    • 对于使用 32 位 SDK 发布的 32 位 (x86) 独立部署,且该 SDK 使用进程内托管模型,请为 32 位启用应用程序池。 在 IIS 管理器中,导航到“连接”边栏中的“应用程序池” 。 选择应用的应用程序池。 在“操作”边栏中,选择,“高级设置” 。 将“启用 32 位应用程序”设置为 True

    • 对于使用进程内托管模型的 64 位 (x64) 独立部署,为 32 位 (x86) 进程禁用应用池。 在 IIS 管理器中,导航到“连接”边栏中的“应用程序池” 。 选择应用的应用程序池。 在“操作”边栏中,选择,“高级设置” 。 将“启用 32 位应用程序”设置为 False

  7. 确认进程模型标识拥有适当的权限。

    如果将应用池的默认标识(“进程模型”>“Identity”)从 ApplicationPoolIdentity 更改为另一标识,请确保新标识拥有对应用文件夹、数据库和其他所需资源的必需访问权限。 例如,应用池需要对文件夹的读取和写入权限,以便应用在其中读取和写入文件。

Windows 身份验证配置(可选)
有关详细信息,请参阅配置 Windows 身份验证

卷影副本

与通过部署应用脱机文件来停止应用相比,将应用程序集卷影复制到 IIS 的 ASP.NET Core 模块 (ANCM) 可以提供更好的最终用户体验。

当 ASP.NET Core 应用在 Windows 上运行时,二进制文件被锁定,因此无法对其进行修改或替换。 卷影复制允许在应用运行时通过复制程序集来更新应用程序集。

卷影复制并不是为了启用零停机部署,因此预计 IIS 仍会回收应用,并且某些请求可能会收到 503 服务不可用响应。 建议使用蓝绿部署Azure 部署槽位这样的模式进行零停机部署。 卷影复制有助于最大限度地减少部署的停机时间,但不能完全消除它。

卷影复制是通过自定义 web.config 中的 ANCM 处理程序设置来启用的:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <remove name="aspNetCore"/>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".logsstdout">
      <handlerSettings>
        <handlerSetting name="enableShadowCopy" value="true" />
        <!-- Ensure that the IIS ApplicationPool identity has permission to this directory -->
        <handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
      </handlerSettings>
    </aspNetCore>
  </system.webServer>
</configuration>