2016-04-11 20 views
5

Sto utilizzando l'attributo Microsofts EnableCors per le mie chiamate Web API. Il comportamento lato client viene eseguito come mi aspetterei: ad es. la chiamata ritorna come fallita quando l'origine non è valida.Impedire all'API Web di eseguire AT ALL se l'origine EnableCors non è valida

Tuttavia, quando inserisco un punto di interruzione all'interno della chiamata del metodo & da un'origine non valida ... il metodo viene comunque eseguito dall'alto verso il basso (anche se il client ottiene un risultato non riuscito). Se l'origine non è valida, non desidero che venga eseguita TUTTO.

la mia domanda è:
Come posso evitare che il metodo Web API da eseguire a tutti se l'EnableCors origine non è valido?

Aiutami Obi-Wan Kenobi ... sei la mia unica speranza.

MY codice è simile:

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

mia configurazione assomiglia:

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 
} 
+0

fa [questo] (http: //www.britishdeveloper .co.uk/2015/12/stop-processing-options-requests-for.html) aiuto? – cableload

+0

+1 Finora, gestisco "tutto" (comprese le richieste senza browser, perché avvengono) tramite token (ovvero "intestazione di autenticazione"). – EdSF

+0

@ EDSF Aiutami a capire cosa significa? –

risposta

2

Come si è visto ...

CORS intestazioni non sono attesi PREVENIRE le chiamate a un controller: MVC o Web API. Si limita a impedire che i RISULTATI vengano restituiti al browser. Il metodo verrà eseguito indipendentemente da cosa ... quindi devi impedire l'esecuzione con altri mezzi.

QUALI ALTRI MEZZI?
Probabilmente è possibile farlo utilizzando uno AuthorizeAttribute. Ma volevo farlo al livello di azione, così ho scelto un ActionFilterAttribute - ed eseguito controlli in in OnActionExecuting

Note sull'utilizzo: L'ActionFilterAttribute

  • Vai avanti e CORS aperti a tutti & poi limitare per azione (in modo che tutti fare un rumore metallico)

  • considera tutte le chiamate provengono da una valida REFERRER

Ciò significa cose come SQL CLR chiamate da un database (che è quello che sto facendo) non funzionerà perché la REFERRER è nullo (a causa di questo, io sarò il distacco di una soluzione di meglio in seguito).

  • Il ICorsPolicyProvider è inutile - sono rimuoverlo (ma incluso qui)

Tutti gli esempi che ho visto la incluse, ma devo ancora trovare uno scenario in cui esso viene chiamato. Il mio costruttore crea già il CorsPolicy & la politica è disponibile per tutta la durata delle chiamate ... quindi il metodo ICorsPolicyProvider sembra abbastanza inutile (al momento).

  • Il TraceHandler implementazione è la mia - andare avanti & utilizzare il proprio posto

  • L'intestazione Access-Control-Allow-Origin doveva essere aggiunto per garantire un comportamento di ritorno messaggio previsto per alcuni clienti

QUI È IL CODICE: The 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 
    } 
} 

ECCO UTILIZZO DEL CODICE: L'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 
    } 
} 

IMPOSTAZIONI DI APPLICAZIONE: La ActionFilterAttribute

<appSettings> 
    <add key="EnableCors.Origins" value="" /> 
    </appSettings>