使集成安全性和 CORS 与 Asp.Net、Angular 6 和 IIS 配合使用



我有一个带有 Angular 前端和 Asp.Net Web 服务的应用程序。我正在使用 Angular 中的 HttpClient 实体进行 Web 服务调用。我已经配置了 CORS,并且我的 GAT 使用以下基本结构在整个应用程序中完美运行

let url = environment.base_url + 'api/SubDoc/GetDocument?docID=' + docID;
this.httpClient.get(url,
{
withCredentials: true
})
.subscribe(
response => {
console.log(response);
newData = response;
this.currentDocument = newData;
}
);

我已经证明 CORS 是有效的,因为我可以从我的本地主机会话或在我们的 QA 和生产服务器上运行的应用程序会话访问相同的服务端点,只要我在对 EnableCors 的调用中正确编辑 Origin 值

config.EnableCors(new EnableCorsAttribute(origins: "http://localhost:2453,http://localhost:2452,http://***,http://10.100.101.46", headers: headers, methods: methods) { SupportsCredentials = true });

标头和方法定义为以下字符串:

headers = "Origin, Content-Type, X-Auth-Token, content-type,application/x-www-form-urlencoded";
methods = "GET,PUT,POST,OPTIONS,DELETE";

但是,我无法使用以下 HttpClient 结构向同一服务发出 PUT 请求

let url = this.base_url + 'api/ContactGroup/Put';
// Try the new HttpClient object
this.httpClient.put(url, body,
{
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
withCredentials: true
})
.subscribe(
response=>{
let temp:number;
temp = parseInt(response.toString(),10);
if(!isNaN(temp)){
this.groupID = temp;          
}
}
);

当我从同一台服务器连接时,此 PUT 请求运行良好(即没有适用的 CORS),但当我尝试从任何其他服务器连接时,会失败并出现预检错误。

无法加载 http://***/api/ContactGroup/Put:对预检请求的响应未通过访问控制检查:请求的资源上不存在"访问控制-允许源"标头。因此,不允许访问源"http://localhost:2452"。

正如我上面解释的,我确实定义了访问控制允许源标头,我相信我已经包含 PUT 和 OPTIONS 作为支持的方法。

我认为该问题与PUT请求未发送凭据有关。GET 请求在请求标头中有一个凭据项,但即使我使用 withCredentials 选项,PUT 也没有。

以下是 PUT 请求的标头的外观:

Request URL: http://reitgc01/REITServicesQA/api/ContactGroup/Put
Request Method: OPTIONS
Status Code: 401 Unauthorized
Remote Address: 10.100.101.46:80
Referrer Policy: no-referrer-when-downgrade
Content-Type: text/html
Provisional headers are shown
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: PUT
DNT: 1
Origin: http://localhost:2452
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36

我现在已经在这个问题上花了好几天的时间,任何帮助将不胜感激。

感谢 sideshowbarker 为我指明了正确的方向。我终于弄清楚了我正在努力解决的根本问题并实施了解决方案。

基本问题是,我们的站点是一个内部站点,它对连接使用集成的安全性,这会导致与 IIS 中的 CORS 和 CORS 支持不兼容。前端是一个带有 ASP.NET Web服务的Angular 6接口。为了使用集成安全性,您必须禁用匿名身份验证。此外,为了使身份验证正常工作,您必须将 {withCredentials:true} 与您的请求一起传递。这适用于 GET 请求,因为 CORS 规范不需要这些请求的预检选项,并且 GET 请求标头将具有正确解析所需的凭据令牌。

PUT请求出现问题。CORS 需要对 PAT 进行印前检查选项请求,但即使您设置了 withCredentials:true 标志,OPTIONS 请求也不包括凭据。这意味着您必须启用匿名身份验证,这将破坏您的集成安全性。它有点像 22 条军规。我无法找到仅为选项启用匿名登录的方法。我确实找到了几个StackOverflow帖子,声称你可以在IIS中做到这一点,但我无法让它工作。我还发现了MS的帖子,指出IIS不支持此功能。这意味着您必须为 OPTIONS 请求编写自定义处理程序并生成适当的响应以满足浏览器的要求。有几篇关于这个主题的帖子,但我发现最好的是 Stu 对这个线程的回应 启用 Windows 身份验证的 IIS 中 CORS 请求的 401 响应

通过一个小的增强,我能够让它为我工作。我需要在我的系统配置中支持多个域,如果我在源字段中放置多个 url,我仍然会收到错误。我使用 Request.Headers 的 Origin 字段来获取当前源,并将该值发送回我构造的响应标头中,如下面的代码所示:

编辑:玩了一段时间后,我意识到原始解决方案存在缺陷。我的原始代码没有明确检查原点以确保它被允许。正如我之前所说,这对于 GET 操作是可以的,因为 GET 仍然会失败(无论如何,OPTIONS 调用对于 GET 是可选的),但对于 PUT,客户端将传递 OPTIONS 调用并发出 PUT 请求并在响应中出现错误。但是,服务器仍将执行 PUT 请求,因为 CORS 是强制执行的客户端。这将导致数据插入或更新,而不应发生。解决方案是根据 web.config 中维护的 CORS 源列表显式检查检测到的源,如果请求源无效,则返回 403 状态和空白源值。更新后的代码如下

protected void Application_BeginRequest(object sender, EventArgs e)
{
// Declare local variables to store the CORS settings loaded from the web.config
String origins;
String headers;
String methods;
// load the CORS settings from the webConfig file
origins = ConfigurationManager.AppSettings["corsOrigin"];
headers = ConfigurationManager.AppSettings["corsHeader"];
methods = ConfigurationManager.AppSettings["corsMethod"];

if (Request.HttpMethod == "OPTIONS")
{
// Get the Origin from the Request header. This should be present for any cross domain requests
IEnumerable<string> headerValues = Request.Headers.GetValues("Origin");
var id = headerValues.FirstOrDefault();
String httpOrigin = null;
// Assign the origin value to the httpOrigin string.
foreach (var origin in headerValues)
{
httpOrigin = origin;
}
// Construct the Access Control headers using the derived origin value and the desired content.
if (httpOrigin == null) httpOrigin = "*";
// Check to see if the origin is included in the CORS origin list
if (origins.IndexOf(httpOrigin) < 0)
{
// This has failed the CORS check so send a blank origin list and a 403 status (forbidden)
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "");
HttpContext.Current.Response.StatusCode = 403;
}
else
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", httpOrigin);
HttpContext.Current.Response.StatusCode = 200;
}
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With,content-type, Content-Type, Accept, X-Token");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");

var httpApplication = sender as HttpApplication;
httpApplication.CompleteRequest();
}
}

此代码已添加到 global.asax.cs 文件中的 Application_BeginRequest 方法中。此外,该方法的签名已修改为包含发送方对象,如下所示: 受保护的无效Application_BeginRequest(对象发送器,事件参数 e) { 现在,这将生成对满足 CORS 要求的浏览器 OPTIONS 请求的响应,并且后续的 GET 仍将得到验证,因此我认为没有任何负面的安全影响。

最后部分是构造 GAT 和 PUT,以包含凭据信息。示例请求如下所示: let url = environment.base_url + 'api/SubDoc/GetDocument?docID=' + docID;

this.httpClient.get(url,
{
withCredentials: true
})
.subscribe(
response => {
console.log(response);
newData = response;
this.currentDocument = newData;
}
);

body = { "GroupID": groupID, "ContactID": contact.ContactID, "IsActive": isActive }
let url = this.base_url + 'api/ContactGroup/Put';
this.httpClient.put(url, body,
{
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
withCredentials: true
})
.subscribe(
response=>{
let temp:number;
temp = parseInt(response.toString(),10);
if(!isNaN(temp)){
this.groupID = temp;          
}
}
);

希望这将为其他人节省我试图让CORS与Angular 6,ASP.NET 和集成安全性一起工作所经历的痛苦和沮丧的日子

这是我解决这个确切示例的方法。 我正在使用启用了WebApi和Windows身份验证的Angular 5。 CORS的内容在这里详细介绍

TL;博士;本文需要匿名(因为 CORS 标头不携带身份验证信息)和 Windows 身份验证(以便您可以保护您的应用程序)。

所以在我的webapi项目中,我有一个CorsConfig类来设置我的CORS

public static class CorsConfig
{
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute(ConfigurationManager.AppSettings["CorsClientUrl"], "*", "*")
{
SupportsCredentials = true
};
config.EnableCors(cors);
}
}

将其注册到您的 HttpConfiguration

CorsConfig.Register(config);

在您的 Web 配置中,在

<authorization>
<allow verbs="OPTIONS" users="*" />
<allow users="<your user list>" />
<deny users="*" />
</authorization>

这将允许所有用户通过您的选项检查。 对于所有其他动词,我拒绝所有人,只允许特定的用户列表。 您可以将其更改为使用角色。

在 Angular 方面,我创建了一个拦截器,它将对所有 http 请求放置凭据。

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class WinAuthInterceptor implements HttpInterceptor {
constructor() { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = req.clone({
withCredentials: true
});
return next.handle(req);
}
}

在提供程序列表的 app.module.ts 中注册此内容

providers: [
{ provide: HTTP_INTERCEPTORS, useClass: WinAuthInterceptor, multi: true }
]

最新更新