我有一个带有Angular前端和Asp.Net Web服务的应用程序。我正在Angular中使用HttpClient实体进行Web服务调用。我已经配置了CORS,并且我的GET使用以下基本结构在整个应用程序中正常运行
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可以正常工作,因为只要在对EnableCors的调用中正确编辑了Origin值,我就可以从localhost会话或在QA和Production服务器上运行的应用程序会话中访问相同的服务端点。 / p>
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;
}
}
);
当我从同一服务器连接时(即没有适用的CORS),此PUT请求可以完美运行,但是当我尝试从任何其他服务器进行连接时,该请求都会失败并显示预检错误。
无法加载http:// *** / api / ContactGroup / Put:对预检请求的响应未通过访问控制检查:所请求的资源上没有'Access-Control-Allow-Origin'标头。因此,不允许访问来源“ http://localhost:2452”。
如上所述,我确实定义了Access-Control-Allow-Origin标头,并且我相信我已经将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
我现在在这个问题上花了很多天,任何帮助将不胜感激。
答案 0 :(得分:1)
感谢sideshowbarker向我指出正确的方向。我终于弄清楚了我正在努力解决的根本问题,并实施了解决方案。
基本问题是,我们的站点是一个内部站点,该站点使用集成的安全性进行连接,并且这与IIS中的CORS和CORS支持不兼容。前端是带有ASP.NET Web服务的Angular 6接口。为了使用集成安全性,您必须禁用匿名身份验证。此外,为了使身份验证正常工作,您必须将{withCredentials:true}与您的请求一起传递。对于GET请求,此方法很好用,因为CORS规范不需要针对这些请求的预检选项,并且GET请求标头将具有正确解析所需的凭据令牌。
问题与PUT请求有关。 CORS要求对PUT进行预检OPTIONS请求,但是即使您设置withCredentials:true标志,OPTIONS请求也不包括凭据。这意味着您必须启用匿名身份验证,这将破坏您的集成安全性。有点麻烦了22.我无法找到一种仅对OPTIONS启用匿名登录的方法。我确实找到了一些StackOverflow帖子,声称您可以在IIS中做到这一点,但我无法使其正常工作。我还从MS找到帖子,指出IIS不支持此功能。这意味着您必须为OPTIONS请求编写一个自定义处理程序,并生成适当的响应以使浏览器满意。关于这个话题有好几篇文章,但是我发现最好的是Stu在这个主题上的回应 401 response for CORS request in IIS with Windows Auth enabled
通过较小的增强,我可以使它为我工作。我的系统配置中需要对多个域的支持,如果我在origin字段中放置多个url,仍然会出现错误。我使用Request.Headers的Origin字段获取当前原点,并将该值发送回构造的响应标头中,如下面的代码所示:
编辑:玩了一段时间后,我意识到原始的解决方案存在缺陷。我的原始代码没有明确检查来源,以确保它被允许。就像我之前说的那样,这对于GET操作是可以的,因为GET仍然会失败(无论如何GET都是可选的OPTIONS调用),但是对于PUT来说,客户端将通过OPTIONS调用并发出PUT请求并在响应中得到错误。但是,由于CORS是在客户端强制执行的,因此服务器仍将执行PUT请求。当不应该发生数据插入或更新时,将导致数据插入或更新。解决方案是相对于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方法中。此外,该方法的签名已修改为包括发送方对象,如下所示: 受保护的void Application_BeginRequest(对象发送者,EventArgs e) { 现在,这将生成对浏览器OPTIONS请求的响应,该请求满足CORS要求,并且后续的GET仍将得到验证,因此我认为不会对安全产生负面影响。
最后一步是构造您的GET和PUT以同时包含凭据信息。请求示例如下所示: 让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和集成安全性一起使用时所经历的痛苦和沮丧
答案 1 :(得分:0)
这是我解决此确切示例的方法。我正在使用启用WebApi和Windows身份验证的Angular 5。 here
详细介绍了CORS的内容TL; DR;这篇文章的内容是您既需要匿名(因为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);
在您的网络配置中
<authorization>
<allow verbs="OPTIONS" users="*" />
<allow users="<your user list>" />
<deny users="*" />
</authorization>
这将使您的OPTIONS检查通过所有用户。对于所有其他动词,我拒绝所有人,只允许特定的用户列表。您可以将其更改为使用角色。
在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 }
]