由 IIS 团队
本文概述了 IIS CORS 模块,并介绍了模块的配置。
功能概述
Microsoft IIS CORS 模块是一个扩展,使网站能够支持 CORS(跨域资源共享)协议。
IIS CORS 模块为 Web 服务器管理员和网站作者提供了一种使应用程序支持 CORS 协议的方法。 借助本模块,开发人员可以将 CORS 逻辑移出其应用程序并依赖于 Web 服务器。 模块对 CORS 请求的处理由配置中定义的规则决定。 可以轻松定义或配置这些 CORS 规则,以便将所有 CORS 协议处理委托给模块。
IIS CORS 模块是服务器端 CORS 组件
CORS 协议控制客户端/服务器通信。 通常,Web 浏览器充当客户端 CORS 组件,而 IIS 服务器在 IIS CORS 模块的帮助下充当服务器端 CORS 组件。
当协议感知客户端(如 Web 浏览器)向与当前域不同的域(源)发出请求时,将发生 CORS 请求。 此场景称为跨源请求。 如果未使用 CORS,客户端将阻止跨域请求。 使用 CORS 模块时,IIS 将通知客户端是否可以基于 IIS 配置执行跨域请求。
CORS 预检请求
CORS 预检请求用于确定所请求的资源是否设置为由服务器跨源共享。 CORS 预检将 HTTP OPTIONS 方法与 ACCESS-CONTROL-REQUEST-METHOD 和 ORIGIN 请求标头配合使用。 IIS CORS 模块设计用于在其他 IIS 模块处理相同请求之前处理 CORS 预检请求。 OPTIONS 请求始终是匿名的,因此 CORS 模块为 IIS 服务器提供了一种方法来正确响应预检请求,即便需要在服务器级别禁用匿名身份验证也是如此。
CORS 配置
IIS CORS 通过站点或应用程序 web.config 文件进行配置,并在其中corssystem.webServer有自己的配置部分。
下面是为名为 contentSite 的网站启用 CORS 的配置示例。 * 源允许所有主机源;但是,稍后将排除从 http://* 头开始的那些。 对于 https://*.microsoft.com 主机源,CORS 响应已被自定义为各种 CORS 配置,作为示例。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<cors enabled="true" failUnlistedOrigins="true">
<add origin="*" />
<add origin="https://*.microsoft.com"
allowCredentials="true"
maxAge="120">
<allowHeaders allowAllRequestedHeaders="true">
<add header="header1" />
<add header="header2" />
</allowHeaders>
<allowMethods>
<add method="DELETE" />
</allowMethods>
<exposeHeaders>
<add header="header1" />
<add header="header2" />
</exposeHeaders>
</add>
<add origin="http://*" allowed="false" />
</cors>
</system.webServer>
</configuration>
使用 IIS CORS 模块,可以:
- 为整个 IIS 服务器或特定 IIS 站点、应用程序、虚拟目录、物理目录或文件(system.webServer/cors)启用、禁用 CORS。
- 将所有源主机域配置为使用 * 源主机规则接受。
- 配置特定源主机域的列表,只允许具有与列出的源主机域之一相同的源请求标头值的 CORS 请求。
- 配置源域列表时,可以为源主机域配置通配符,例如
http://*或https://*.mydomain.com。 - 应配置不允许作为 CORS 请求的源站域名列表。
- 使用配置的值自定义 CORS 响应标头值。
cors 元素的属性
| 属性 | 说明 |
|---|---|
enabled |
可选布尔属性。 指定是否启用 CORS。 默认值为 false。 |
failUnlistedOrigins |
可选布尔属性。 指定如果请求的源与配置的源列表不匹配,或者源主机被配置为不允许,则是否使用403设置CORS响应状态代码。 默认值为 false。 |
添加源规则 <添加>
源点规则
集合 <add> 的 <cors> 元素指定要添加到源规则列表中的单个源。
源规则的属性
| 属性 | 说明 |
|---|---|
origin |
必需的字符串属性。 指定要对其施加源规则的源主机。 可以使用星号 \ 将此规则应用于所有源请求标头值。 还可以使用星号 \ 作为子子域名称的通配符。 如果有多个源规则,则无论允许的属性值如何,它都应用于最具体的源主机名规则。 |
allowed |
可选布尔属性。 指定是否接受源主机的 CORS 请求。 默认值为 true。 |
allowCredentials |
可选布尔属性。 指定是否设置 Access-Control-Allow-Credentials: true CORS 响应标头。 此属性应仅用于特定源主机名,而不是 * 源主机,以便符合 CORS 协议。默认值为 false。 |
maxAge |
可选的整数属性。 持续时间(以秒为单位)。 指定预检 CORS 请求的响应标头的值为 Access-Control-Max-Age。 访问控制-Max-Age 响应标头应仅针对 CORS 预检请求进行设置。 如果不想在 CORS 响应中设置 Access-Control-Max-Age 标头,请为此属性设置 -1。默认值为 -1。 |
仅使用 * 源主机规则
如果只有 * 源主机规则,则与特定源主机名规则相比,IIS CORS 模块具有一些不同的行为。 如果只有 * 源主机规则,IIS CORS 模块将执行以下操作:
- 无论客户端 CORS 组件发送的请求标头的值如何,Access-Control-Allow-Origin 响应标头的值
origin都设置为 * 。 - Vary:
origin响应标头未被添加,因为 IIS CORS 不会生成除 * 之外的 Access-Control-Allow-Origin 响应标头值,并且不需要使用 Vary:origin响应标头值。
源主机规则的子元素
| 元素 | 说明 |
|---|---|
| allowHeaders | 配置 allowHeaders 集合,该集合用于源主机规则中指定的 源 主机的 Access-Control-Allow-Headers CORS 响应头的值。响应标头将仅针对实际 CORS 请求而不是预检请求进行设置。 |
| allowMethods | 配置 allowMethods 集合,用于指定源主机规则中origin主机的 CORS 响应标头Access-Control-Allow-Methods的值。Access-Control-Allow-Methods响应标头将仅针对 CORS 预检请求设置。 |
| exposeHeaders | 配置公开标头集合,使其用于在源主机规则中指定的源主机的 CORS 响应标头Access-Control-Expose-Headers。仅针对实际的 CORS 请求而不是预检请求设置 Access-Control-Expose-Headers 响应标头。 |
allowHeaders 元素的属性
| 属性 | 说明 |
|---|---|
allowAllRequestedHeaders |
可选布尔属性。 如果为 true,IIS 模块将采用给定的 Access-Control-Request-Headers CORS 请求标头的值,并使用相同的值设置 Access-Control-Allow-Headers 响应标头,这意味着允许所有给定标头。 如果为 false,则它使用 allowHeaders 集合的标头值设置 Access-Control-Allow-Headers 响应标头,这意味着只允许列出的标头。 默认值为 false。 |
示例代码
C#
using System;
using System.Text;
using Microsoft.Web.Administration;
internal static class Sample {
private static void Main() {
using(ServerManager serverManager = new ServerManager()) {
Configuration config = serverManager.GetWebConfiguration("contentSite");
ConfigurationSection corsSection = config.GetSection("system.webServer/cors");
corsSection["enabled"] = true;
corsSection["failUnlistedOrigins"] = true;
ConfigurationElementCollection corsCollection = corsSection.GetCollection();
ConfigurationElement addElement = corsCollection.CreateElement("add");
addElement["origin"] = @"*";
corsCollection.Add(addElement);
ConfigurationElement addElement1 = corsCollection.CreateElement("add");
addElement1["origin"] = @"https://*.microsoft.com";
addElement1["allowCredentials"] = true;
addElement1["maxAge"] = 120;
ConfigurationElement allowHeadersElement = addElement1.GetChildElement("allowHeaders");
allowHeadersElement["allowAllRequestedHeaders"] = true;
ConfigurationElementCollection allowHeadersCollection = allowHeadersElement.GetCollection();
ConfigurationElement addElement2 = allowHeadersCollection.CreateElement("add");
addElement2["header"] = @"header1";
allowHeadersCollection.Add(addElement2);
ConfigurationElement addElement3 = allowHeadersCollection.CreateElement("add");
addElement3["header"] = @"header2";
allowHeadersCollection.Add(addElement3);
ConfigurationElementCollection allowMethodsCollection = addElement1.GetCollection("allowMethods");
ConfigurationElement addElement4 = allowMethodsCollection.CreateElement("add");
addElement4["method"] = @"DELETE";
allowMethodsCollection.Add(addElement4);
ConfigurationElementCollection exposeHeadersCollection = addElement1.GetCollection("exposeHeaders");
ConfigurationElement addElement5 = exposeHeadersCollection.CreateElement("add");
addElement5["header"] = @"header1";
exposeHeadersCollection.Add(addElement5);
ConfigurationElement addElement6 = exposeHeadersCollection.CreateElement("add");
addElement6["header"] = @"header2";
exposeHeadersCollection.Add(addElement6);
corsCollection.Add(addElement1);
ConfigurationElement addElement7 = corsCollection.CreateElement("add");
addElement7["origin"] = @"http://*";
addElement7["allowed"] = false;
corsCollection.Add(addElement7);
serverManager.CommitChanges();
}
}
}
JavaScript
var adminManager = new ActiveXObject('Microsoft.ApplicationHost.WritableAdminManager');
adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST/contentSite";
var corsSection = adminManager.GetAdminSection("system.webServer/cors", "MACHINE/WEBROOT/APPHOST/contentSite");
corsSection.Properties.Item("enabled").Value = true;
corsSection.Properties.Item("failUnlistedOrigins").Value = true;
var corsCollection = corsSection.Collection;
var addElement = corsCollection.CreateNewElement("add");
addElement.Properties.Item("origin").Value = "*";
corsCollection.AddElement(addElement);
var addElement1 = corsCollection.CreateNewElement("add");
addElement1.Properties.Item("origin").Value = "https://*.microsoft.com";
addElement1.Properties.Item("allowCredentials").Value = true;
addElement1.Properties.Item("maxAge").Value = 120;
var allowHeadersElement = addElement1.ChildElements.Item("allowHeaders");
allowHeadersElement.Properties.Item("allowAllRequestedHeaders").Value = true;
var allowHeadersCollection = allowHeadersElement.Collection;
var addElement2 = allowHeadersCollection.CreateNewElement("add");
addElement2.Properties.Item("header").Value = "header1";
allowHeadersCollection.AddElement(addElement2);
var addElement3 = allowHeadersCollection.CreateNewElement("add");
addElement3.Properties.Item("header").Value = "header2";
allowHeadersCollection.AddElement(addElement3);
var allowMethodsCollection = addElement1.ChildElements.Item("allowMethods").Collection;
var addElement4 = allowMethodsCollection.CreateNewElement("add");
addElement4.Properties.Item("method").Value = "DELETE";
allowMethodsCollection.AddElement(addElement4);
var exposeHeadersCollection = addElement1.ChildElements.Item("exposeHeaders").Collection;
var addElement5 = exposeHeadersCollection.CreateNewElement("add");
addElement5.Properties.Item("header").Value = "header1";
exposeHeadersCollection.AddElement(addElement5);
var addElement6 = exposeHeadersCollection.CreateNewElement("add");
addElement6.Properties.Item("header").Value = "header2";
exposeHeadersCollection.AddElement(addElement6);
corsCollection.AddElement(addElement1);
var addElement7 = corsCollection.CreateNewElement("add");
addElement7.Properties.Item("origin").Value = "http://*";
addElement7.Properties.Item("allowed").Value = false;
corsCollection.AddElement(addElement7);
adminManager.CommitChanges();
命令行 (AppCmd)
appcmd.exe set config "contentSite" -section:system.webServer/cors /enabled:"True" /failUnlistedOrigins:"True"
appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='*']"
appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120']"
appcmd.exe set config "contentSite" -section:system.webServer/cors /[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].allowHeaders.allowAllRequestedHeaders:"True"
appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].allowHeaders.[header='header1']"
appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].allowHeaders.[header='header2']"
appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].allowMethods.[method='DELETE']"
appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].exposeHeaders.[header='header1']"
appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='https://*.microsoft.com',allowCredentials='True',maxAge='120'].exposeHeaders.[header='header2']"
appcmd.exe set config "contentSite" -section:system.webServer/cors /+"[origin='http://*',allowed='False']"
PowerShell
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors" -name "enabled" -value "True"
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors" -name "failUnlistedOrigins" -value "True"
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors" -name "." -value @{origin='*'}
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors" -name "." -value @{origin='https://*.microsoft.com';allowCredentials='True';maxAge=120}
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/allowHeaders" -name "allowAllRequestedHeaders" -value "True"
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/allowHeaders" -name "." -value @{header='header1'}
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/allowHeaders" -name "." -value @{header='header2'}
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/allowMethods" -name "." -value @{method='DELETE'}
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/exposeHeaders" -name "." -value @{header='header1'}
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors/add[@origin='https://*.microsoft.com']/exposeHeaders" -name "." -value @{header='header2'}
Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/contentSite' -filter "system.webServer/cors" -name "." -value @{origin='http://*';allowed='False'}