我正在构建一个WCF自托管数据服务(顺便提一下OData),我正在使用基本身份验证来验证用户身份。这不是很难,我只需要一些配置步骤,构建一个UserNamePasswordValidator和一个IAuthorizationPolicy - DONE。
现在我需要支持CORS(Cross-Origin Resource Sharing)。我尝试了很多实现,有些已经记录(例如,this),其他的由我自己制作。
问题是,如果我启用了基本身份验证,因为CORS预检请求(OPTIONS)没有'授权'标头,我无法操纵请求(当然,或者那会破坏浏览器的目的),我无法拦截/响应服务器上的请求。我甚至无法检查它到底有多远!我试图实现许多行为,绑定,管理器等,但我无法捕获该请求,甚至没有捕获“DataService<> .OnStartProcessingRequest()”。
如果我在服务器端禁用Basic Auth ,我能够捕获CORS预检请求并最终响应它(使用IDispatchMessageInspector和BehaviorExtensionElement),但这样我必须我自己实现Basic Auth ...该死的。
请帮帮我。 我如何实现这两个?如何在Basic Auth简单响应401 Unauthorized之前拦截CORS预检请求?
提前致谢。
答案 0 :(得分:0)
首先,您可以处理所有“ OPTIONS ”请求以允许所有这些请求。
我使用以下技巧:
我的服务界面:
/// <summary>Handles ALL the http options requests.</summary>
[WebInvoke(Method = "OPTIONS", UriTemplate = "*")]
bool HandleHttpOptionsRequest();
实施:
/// <summary>Handles ALL the http options requests.</summary>
public bool HandleHttpOptionsRequest()
{
if (WebOperationContext.Current != null && WebOperationContext.Current.IncomingRequest.Method == "OPTIONS")
{
return true;
}
return false;
}
第二,您需要在“CORS enabler”enable-cors
中添加 Access-Control-Allow-Credentialspublic void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
var requiredHeaders = new Dictionary<string, string>
{
{ "Access-Control-Allow-Credentials", "true" },
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS" },
{ "Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Safe-Repository-Path,Safe-Repository-Token" }
};
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders));
}
答案 1 :(得分:0)
我如何在下面提到的情况下解决此问题-
/// <summary>
/// Class written to check for whether the REST calls is authorised.
/// </summary>
public class RestServiceAuthorizationManger : ServiceAuthorizationManager
{
/// <summary>
/// Method to check for basic authorization in rest service calls.
/// </summary>
protected override bool CheckAccessCore(OperationContext operationContext)
{
try
{
bool Verified = false;
if (WebOperationContext.Current != null &&
WebOperationContext.Current.IncomingRequest.Method == "OPTIONS")
{
WebOperationContext.Current.OutgoingResponse.StatusCode =
HttpStatusCode.OK;
return true;
}
else
{
//Extract the Authorization header, and parse out the credentials converting the Base64 string:
var authHeader = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
if ((authHeader != null) && (authHeader != string.Empty))
{
//You code to check for authorization credientials from incomming authorization headers.
}
else
{
//Throw an exception with the associated HTTP status code equivalent to HTTP status 401
//No authorization header was provided, so challenge the client to provide before proceeding:
//WebOperationContext.Current.OutgoingResponse.Headers.Add("WWW-Authenticate: Basic realm=\"RestServiceAuthorizationManger\"");
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
WebOperationContext.Current.OutgoingResponse.StatusDescription = "Unauthorized";
return false;
}
}
}
catch(Exception ex)
{
//Throw an exception with the associated HTTP status code equivalent to HTTP status 401
//No authorization header was provided, so challenge the client to provide before proceeding:
//WebOperationContext.Current.OutgoingResponse.Headers.Add("WWW-Authenticate: Basic realm=\"RestServiceAuthorizationManger\"");
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
WebOperationContext.Current.OutgoingResponse.StatusDescription = "Unauthorized";
return false;
}
}
}
public class EnableCorsSupportBehavior : IEndpointBehavior
{
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(
new CorsEnablingMessageInspector());
}
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
}
}
public class CorsEnablingMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request,
IClientChannel channel, InstanceContext instanceContext)
{
var httpRequest = (HttpRequestMessageProperty)request
.Properties[HttpRequestMessageProperty.Name];
return new
{
origin = httpRequest.Headers["Origin"],
handlePreflight = httpRequest.Method.Equals("OPTIONS",
StringComparison.InvariantCultureIgnoreCase)
};
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
var state = (dynamic)correlationState;
// handle request preflight
if (state != null && state.handlePreflight)
{
reply = Message.CreateMessage(MessageVersion.None, "PreflightReturn");
var httpResponse = new HttpResponseMessageProperty();
reply.Properties.Add(HttpResponseMessageProperty.Name, httpResponse);
httpResponse.SuppressEntityBody = true;
httpResponse.StatusCode = HttpStatusCode.OK;
}
// add allowed origin info
var response = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name];
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH, OPTIONS");
response.Headers.Add("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization");
response.Headers.Add("Access-Control-Allow-Credentials", "true");
}
}
希望这可能对某人有所帮助。谢谢!