如果EnableCors Origin无效,则阻止Web API执行AT ALL

时间:2016-04-11 18:09:18

标签: c# asp.net-mvc asp.net-web-api cors

我正在使用Microsofts EnableCors属性进行Web API次调用。客户端行为按我的预期运行:例如当Origin无效时,调用返回失败。

然而,当我在方法中加入一个断点时,&从无效的Origin调用...该方法仍然从上到下执行(即使客户端获得失败的结果)。如果Origin无效,我不希望它执行AT ALL。

我的问题是:
如果EnableCors Origin无效,如何阻止Web API方法执行AT ALL?

帮助我Obi-Wan Kenobi ......你是我唯一的希望。

我的代码看起来像:

[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));
}

MY CONFIG看起来像:

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
}

1 个答案:

答案 0 :(得分:2)

事实证明......

预计不会对控制器进行PREVENT调用:MVC或Web API。它只是阻止结果返回到浏览器。它将被执行的方法无论如何......所以你必须通过其他方式阻止执行。

其他方面是什么?
您可以使用AuthorizeAttribute来完成此操作。但我想在ACTION级别进行,所以我选择了ActionFilterAttribute - 并在OnActionExecuting中执行了检查

使用注意事项: ActionFilterAttribute

  • 继续向所有人开放CORS&amp;然后按行动限制(所以每个人都可以PING)

  • 假设所有来电均来自有效的REFERRER

这意味着来自数据库(我正在做的)的SQL CLR调用之类的东西将无效,因为REFERRER为空(因此,我将在稍后发布更好的解决方案。)

  • ICorsPolicyProvider没用 - 我正在删除它(但在此处包含它)

我看到的所有例子都包括它,但我还没有找到一个被调用的场景。我的构造函数已经创建了CorsPolicy&amp;该策略在整个呼叫生命周期内都可用...所以ICorsPolicyProvider方法似乎没用(目前)。

  • TraceHandler实施是我自己的 - 继续&amp;使用你自己的

  • 必须添加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>