如果 EnableCors Origin 无效,则完全阻止 Web API 执行
Posted
技术标签:
【中文标题】如果 EnableCors Origin 无效,则完全阻止 Web API 执行【英文标题】:Preventing Web API from executing AT ALL if the EnableCors Origin is invalid 【发布时间】:2016-08-02 01:13:10 【问题描述】:我正在为我的Web API
调用使用Microsofts EnableCors
属性。客户端行为按我的预期运行:例如当 Origin 无效时调用返回失败。
但是,当我在方法中设置断点并从无效的 Origin 调用时...该方法仍然从上到下执行(即使客户端得到失败的结果)。如果 Origin 无效,我根本不希望它执行。
我的问题是: 如果 EnableCors Origin 无效,如何阻止 Web API 方法执行?
帮助我欧比旺·克诺比……你是我唯一的希望。
我的代码看起来像:
[HttpPost]
[EnableCors(origins: "www.zippitydoodah.com", headers: "*", methods: "*")]
public HttpResponseMessage Enqueue(HttpRequestMessage request)
// NONE OF THIS SHOULD RUN: If the Origin is bad...but (oddly) it is
TraceHandler.TraceIn(TraceLevel.Info);
string claimId = string.Empty;
ClaimMessage claimMessage = null;
try
claimId = GetClaimId(request);
claimMessage = CreateClaimMessage(claimId, segmentClaimFullName);
Enqueue(claimMessage);
TraceHandler.TraceAppend(FORMAT_ENQUEUED_SUCCESS, claimId);
catch (Exception ex)
TraceHandler.TraceError(ex);
TraceHandler.TraceOut();
EnqueueToPoison(ex, claimMessage);
return Request.CreateResponse(HttpStatusCode.InternalServerError, GetHttpError());
TraceHandler.TraceOut();
return Request.CreateResponse(HttpStatusCode.OK, string.Format(FORMAT_ENQUEUED_SUCCESS, claimId));
我的配置看起来像:
public static class WebApiConfig
#region <Methods>
public static void Register(HttpConfiguration config)
// ENABLE CORS
config.EnableCors();
// CREATE ROUTES
config.Routes.MapHttpRoute(
name: "DefaultRpcApiActions",
routeTemplate: "api/controller/actions/action/id",
defaults: new id = RouteParameter.Optional );
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/controller/id",
defaults: new id = RouteParameter.Optional );
#endregion
【问题讨论】:
this 有帮助吗? +1 到目前为止,我通过令牌(又名“身份验证标头”)处理“一切”(包括非浏览器请求,因为它们将发生)。 @EdSF 帮我理解这意味着什么? 它与任何其他“身份验证”没有什么不同 - 例如Basic
, "shared secret"/tokens 等等。所以验证请求在任何其他执行之前(验证检查将发生,但没有其他),无论是来自浏览器/XHR 还是其他一些非-浏览器客户端(curl
、HttpClient
、WebClient
等执行 POST
、GET
等)。本质上,CORS 处理 javascript/XHR,但不处理直接请求(例如 Form Post 等),因此如果您想停止执行,则需要更进一步。这是detail that will be helpful。 Hth
【参考方案1】:
事实证明...
CORS 标头不应阻止对控制器的调用:MVC 或 Web API。它只是防止将结果返回给浏览器。无论如何都会被执行的方法……所以你必须通过其他方式阻止执行。
还有什么意思?
您可以使用AuthorizeAttribute
来完成。但我想在 ACTION 级别进行,所以我选择了ActionFilterAttribute
- 并在OnActionExecuting
中进行了签入
使用注意事项: ActionFilterAttribute
继续向所有人开放 CORS,然后通过操作进行限制(这样每个人都可以 PING)
假设所有呼叫都来自有效的REFERRER
这意味着像 SQL CLR
来自数据库的调用(这是我正在做的)将不起作用,因为 REFERRER
为空(因此,我将发布一个以后有更好的解决方案。
ICorsPolicyProvider
没用 - 我正在删除它(但包含在此处)
我看到的所有示例都包含它,但我还没有找到调用它的场景。我的构造函数已经创建了 CorsPolicy
并且该策略在整个调用生命周期中都可用...所以 ICorsPolicyProvider
方法似乎毫无用处(目前)。
TraceHandler
实现是我自己的 - 继续使用您自己的实现
必须添加 Access-Control-Allow-Origin
标头以确保某些客户端的预期返回消息行为
代码如下: ActionFilterAttribute
namespace My.Application.Security
using My.Application.Diagnostics;
using System;
using System.Configuration;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Cors;
using System.Web.Http.Controllers;
using System.Web.Http.Cors;
using System.Web.Http.Filters;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class EnableWebApiCorsFromAppSettingsAttribute : ActionFilterAttribute, ICorsPolicyProvider
#region <Fields & Constants>
private const string EXCEPTION_CONTEXT_NULL = "Access Denied: HttpActionContext cannot be null.";
private const string EXCEPTION_REFERRER_NULL = "Access Denied: Referrer cannot be null.";
private const string FORMAT_INVALID_REFERRER = "Access Denied: '0' is not a valid referrer.";
private const string FORMAT_REFERRER = "Referrer: '0' was processed for this request.";
private const string FORMAT_REFERRER_FOUND = "Referrer IsFound: 0.";
private readonly CorsPolicy policy;
#endregion
#region <Constructors>
public EnableWebApiCorsFromAppSettingsAttribute(string appSettingKey, bool allowAnyHeader = true, bool allowAnyMethod = true, bool supportsCredentials = true)
policy = new CorsPolicy();
policy.AllowAnyOrigin = false;
policy.AllowAnyHeader = allowAnyHeader;
policy.AllowAnyMethod = allowAnyMethod;
policy.SupportsCredentials = supportsCredentials;
SetValidOrigins(appSettingKey);
if (policy.Origins.Count == 0)
policy.AllowAnyOrigin = true;
#endregion
#region <Methods>
#region public
public override void OnActionExecuting(HttpActionContext actionContext)
TraceHandler.TraceIn(TraceLevel.Info);
if (actionContext == null)
throw new ArgumentNullException("HttpActionContext");
if (actionContext.Request.Headers.Referrer == null)
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, EXCEPTION_REFERRER_NULL);
var referrer = actionContext.Request.Headers.Referrer.ToString();
TraceHandler.TraceAppend(string.Format(FORMAT_REFERRER, referrer));
// If no Origins Are Set - Do Nothing
if (policy.Origins.Count > 0)
var isFound = policy.Origins.Contains(referrer);
TraceHandler.TraceAppend(string.Format(FORMAT_REFERRER_FOUND, isFound));
if (!isFound)
TraceHandler.TraceAppend("IsFound was FALSE");
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, string.Format(FORMAT_INVALID_REFERRER, referrer));
TraceHandler.TraceOut();
base.OnActionExecuting(actionContext);
public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
if (cancellationToken.CanBeCanceled && cancellationToken.IsCancellationRequested)
return Task.FromResult<CorsPolicy>(null);
return Task.FromResult(policy);
#endregion
#region private
private void SetValidOrigins(string appSettingKey)
// APP SETTING KEY: <add key="EnableCors.Origins" value="http://www.zippitydoodah.com" />
var origins = string.Empty;
if (!string.IsNullOrEmpty(appSettingKey))
origins = ConfigurationManager.AppSettings[appSettingKey];
if (!string.IsNullOrEmpty(origins))
foreach (string origin in origins.Split(",;|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
policy.Origins.Add(origin);
#endregion
#endregion
代码用法如下: ActionFilterAttribute
namespace My.Application.Web.Controllers
using Security;
using My.Application.Diagnostics;
using My.Application.Framework.Configuration;
using My.Application.Models;
using My.Application.Process;
using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Messaging;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
using System.Xml.Linq;
using System.Xml.Serialization;
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class OutboundEventController : ApiControllerBase
#region <Actions>
[HttpGet]
public HttpResponseMessage Ping()
TraceHandler.TraceIn(TraceLevel.Info);
if (Request.Headers.Referrer == null)
TraceHandler.TraceAppend(MESSAGE_REFERRER_NULL);
if (Request.Headers.Referrer != null)
TraceHandler.TraceAppend(string.Format(FORMAT_REFERRER, Request.Headers.Referrer));
TraceHandler.TraceOut();
return Request.CreateResponse(HttpStatusCode.OK, "Ping back at cha...");
[HttpPost]
[EnableWebApiCorsFromAppSettings("EnableCors.Origins")]
public HttpResponseMessage Enqueue(HttpRequestMessage request)
TraceHandler.TraceIn(TraceLevel.Info);
if (Request.Headers.Referrer == null)
TraceHandler.TraceAppend(MESSAGE_REFERRER_NULL);
if (Request.Headers.Referrer != null)
TraceHandler.TraceAppend(string.Format(FORMAT_REFERRER, Request.Headers.Referrer));
try
// Do Amazing Stuff Here...
TraceHandler.TraceAppend(FORMAT_ENQUEUED_SUCCESS, claimId);
catch (Exception ex)
TraceHandler.TraceError(ex);
TraceHandler.TraceOut();
EnqueueToPoison(ex, claimMessage);
return Request.CreateResponse(HttpStatusCode.InternalServerError, GetHttpError());
TraceHandler.TraceOut();
// FORCE: Correct Header
var response = Request.CreateResponse(HttpStatusCode.OK, string.Format(FORMAT_ENQUEUED_SUCCESS, claimId));
response.Headers.Add("Access-Control-Allow-Origin", "*");
return response;
#endregion
private string GetClaimId(HttpRequestMessage request)
var stream = request.Content.ReadAsStreamAsync().Result;
var xdoc = XDocument.Load(stream);
var result = GetElementValue(xdoc, "ClaimId");
return result;
private ClaimMessage CreateClaimMessage(string claimId, string process)
ClaimMessage message = new ClaimMessage();
message.ClaimID = claimId;
message.Process = process;
return message;
private void Enqueue(ClaimMessage claimMessage)
var queueName = ConfigurationManager.AppSettings[Settings.Messaging.Queue.Name].ToString();
var queue = new MessageQueue(queueName);
queue.DefaultPropertiesToSend.Recoverable = true;
TraceHandler.TraceAppend(FORMAT_QUEUE_NAME, queueName);
MessageQueueTransaction transaction;
transaction = new MessageQueueTransaction();
transaction.Begin();
var message = new System.Messaging.Message();
message.Formatter = new XmlMessageFormatter(new Type[] typeof(ClaimMessage) );
message.Label = "ClaimID " + claimMessage.ClaimID;
message.Body = claimMessage;
queue.Send(message, transaction);
transaction.Commit();
queue.Close();
private void EnqueueToPoison(Exception exception, ClaimMessage claimdata)
TraceHandler.TraceIn(TraceLevel.Info);
var poison = ToPoisonMessage(exception, claimdata);
var message = new System.Messaging.Message();
try
var poisonQueueName = ConfigurationManager.AppSettings[Settings.Messaging.PoisonQueue.Name].ToString();
TraceHandler.TraceAppend(FORMAT_QUEUE_NAME, poisonQueueName);
if (MessageQueue.Exists(poisonQueueName))
var queue = new MessageQueue(poisonQueueName);
queue.DefaultPropertiesToSend.Recoverable = true;
var transaction = new MessageQueueTransaction();
transaction.Begin();
message.Formatter = new XmlMessageFormatter(new Type[] typeof(PoisonClaimMessage) );
message.Label = "Poison ClaimID " + poison.ClaimID;
var xmlSerializer = new XmlSerializer(poison.GetType());
xmlSerializer.Serialize(message.BodyStream, poison);
queue.Send(message, transaction);
TraceHandler.TraceAppend(FORMAT_ENQUEUED_POISON_SUCCESS, poison.ClaimID);
transaction.Commit();
queue.Close();
catch(Exception ex)
// An error occurred while enqueuing to POISON
var poisonXml = ToString(poison);
TraceHandler.TraceError(ex);
TraceHandler.TraceAppend(poisonXml);
finally
TraceHandler.TraceOut();
#endregion
应用设置: ActionFilterAttribute
<appSettings>
<add key="EnableCors.Origins" value="" />
</appSettings>
【讨论】:
以上是关于如果 EnableCors Origin 无效,则完全阻止 Web API 执行的主要内容,如果未能解决你的问题,请参考以下文章
经实测解决Access-Control-Allow-Origin多域名跨域问题
Web API 2 启用 CORS 引发 Access-Control-Allow-Origin 错误