J'ai un service WebAPI 2 REST exécuté avec l'authentification Windows. Il est hébergé séparément du site Web. J'ai donc activé CORS à l'aide du package ASP.NET CORS NuGet. Mon site client utilise AngularJS.
Jusqu'ici, voici ce que j'ai vécu:
Là où je suis coincé - mes _ appels de contrôle en amont Angular n'incluent pas les informations d'identification. Selon cette réponse , cela est inhérent au projet, car les requêtes OPTIONS sont conçues pour être anonymes. Cependant, l'authentification Windows arrête la demande avec un 401.
J'ai essayé de mettre l'attribut [AllowAnonymous] sur mon MessageHandler. Cela fonctionne sur mon ordinateur de développeur - les verbes OPTIONS ne nécessitent pas d’authentification, contrairement aux autres. Cependant, lorsque je compile et déploie sur le serveur de test, je continue à recevoir un 401 avec ma demande OPTIONS.
Est-il possible d'appliquer [AllowAnonymous] sur mon MessageHandler lors de l'utilisation de l'authentification Windows? Si oui, des conseils sur la façon de le faire? Ou est-ce le mauvais trou de lapin et je devrais envisager une approche différente?
UPDATE: J'ai pu le faire fonctionner en définissant l'authentification Windows et l'authentification anonyme sur le site dans IIS. Cela a permis à tout d'autoriser les anonymes. J'ai donc ajouté un filtre global d'autorisation, tout en conservant l'option AllowAnonymous sur mon MessageHandler.
Cependant, cela ressemble à un hack ... J'ai toujours compris qu'il ne fallait utiliser qu'une seule méthode d'authentification (pas de méthode mixte). Si quelqu'un a une meilleure approche, j'aimerais en entendre parler.
J'ai utilisé l'auto-hébergement avec HttpListener et la solution suivante a fonctionné pour moi:
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
var listener = appBuilder.Properties["System.Net.HttpListener"] as HttpListener;
if (listener != null)
{
listener.AuthenticationSchemeSelectorDelegate = (request) => {
if (String.Compare(request.HttpMethod, "OPTIONS", true) == 0)
{
return AuthenticationSchemes.Anonymous;
}
else
{
return AuthenticationSchemes.IntegratedWindowsAuthentication;
}};
}
J'ai eu du mal à faire en sorte que les demandes de la SCRO répondent aux contraintes suivantes (très similaires à celles des PO):
Ma configuration finale est la suivante:
web.config - autorise les requêtes de contrôle en amont non authentifiées (anonymes) (OPTIONS)
<system.web>
<authentication mode="Windows" />
<authorization>
<allow verbs="OPTIONS" users="*"/>
<deny users="?" />
</authorization>
</system.web>
global.asax.cs - répondre correctement avec des en-têtes permettant à l'appelant d'un autre domaine de recevoir des données
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.Request.HttpMethod == "OPTIONS")
{
if (Context.Request.Headers["Origin"] != null)
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, MaxDataServiceVersion");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Response.End();
}
}
CORS permettant
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// all requests are enabled in this example. SupportsCredentials must be here to allow authenticated requests
var corsAttr = new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true };
config.EnableCors(corsAttr);
}
}
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
Je l'ai résolu de manière très similaire mais avec quelques détails et concentré sur le service oData
Je n'ai pas désactivé l'authentification anonyme dans IIS, car j'en avais besoin pour POST demander
Et j'ai ajouté dans Global.aspx (Ajout de MaxDataServiceVersion
dans Access-Control-Allow-Headers
) le même code que ci-dessus
protected void Application_BeginRequest(object sender, EventArgs e)
{
if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
{
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,MaxDataServiceVersion");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Context.Response.End();
}
}
et WebAPIConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
et appel AngularJS
$http({
method: 'POST',
url: 'http://XX.XXX.XXX.XX/oData/myoDataWS.svc/entityName',
withCredentials: true,
headers: {
'Content-Type': 'application/json;odata=verbose',
'Accept': 'application/json;odata=light;q=1,application/json;odata=verbose;q=0.5',
'MaxDataServiceVersion': '3.0'
},
data: {
'@odata.type':'entityName',
'field1': 1560,
'field2': 24,
'field3': 'sjhdjshdjsd',
'field4':'wewewew',
'field5':'ewewewe',
'lastModifiedDate':'2015-10-26T11:45:00',
'field6':'1359',
'field7':'5'
}
});
Il s'agit d'une solution beaucoup plus simple: quelques lignes de code permettant à toutes les requêtes "OPTIONS" d'emprunter l'identité du compte de pool d'applications. Vous pouvez laisser Anonymous désactivé et configurer les stratégies CORS conformément aux pratiques habituelles, mais ajoutez ensuite les éléments suivants à votre fichier global.asax.cs:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.Request.HttpMethod == "OPTIONS" && Context.User == null)
{
Context.User = System.Security.Principal.WindowsPrincipal.Current;
}
}
Ceci est ma solution.
Global.asax *
protected void Application_BeginRequest(object sender, EventArgs e)
{
if(!ListOfAuthorizedOrigins.Contains(Context.Request.Headers["Origin"])) return;
if (Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
HttpContext.Current.Response.StatusCode = 200;
HttpContext.Current.Response.End();
}
if (Request.Headers.AllKeys.Contains("Origin"))
{
HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
}
}
Les autres solutions trouvées sur le Web ne me convenaient pas ou semblaient trop compliquées; à la fin je suis venu avec une solution plus simple et efficace:
web.config:
<system.web>
...
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
</system.web>
Propriétés du projet:
Windows Authentication
Anonymous Authentication
Setup CORS:
[Assembly: OwinStartup(typeof(Startup))]
namespace MyWebsite
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
Cela nécessite un assemblage Microsoft.Owin.Cors disponible sur NUget.
Angular initialisation:
$httpProvider.defaults.withCredentials = true;
désactivez l'authentification anonyme dans IIS si vous n'en avez pas besoin.
Puis ajoutez ceci dans votre asax global:
protected void Application_BeginRequest(object sender, EventArgs e)
{
if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
{
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Context.Response.End();
}
}
Assurez-vous que lorsque vous activez cors, vous activez également l'utilisation des informations d'identification, par exemple:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Comme vous pouvez le constater, j'active CORS globalement et, à l'aide de l'application BeginRequest hook, j'authentifie toutes les demandes OPTIONS pour l'API (Web Api) et les demandes Odata (si vous l'utilisez).
Cela fonctionne très bien avec tous les navigateurs, côté client, n'oubliez pas d'ajouter le xhrFiled withCredentials comme indiqué ci-dessous.
$.ajax({
type : method,
url : apiUrl,
dataType : "json",
xhrFields: {
withCredentials: true
},
async : true,
crossDomain : true,
contentType : "application/json",
data: data ? JSON.stringify(data) : ''
}).....
J'essaie de trouver une autre solution en évitant d'utiliser le crochet, mais sans succès jusqu'à présent, J'utiliserais la configuration web.config pour effectuer les opérations suivantes: T TRAVAIL!
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<authentication mode="Windows" />
<authorization>
<deny verbs="GET,PUT,POST" users="?" />
<allow verbs="OPTIONS" users="?"/>
</authorization>
</system.web>
<location path="api">
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
Dave,
Après avoir joué avec le paquet CORS, voici ce qui m’a fait marcher: [EnableCors (origines: "", en-têtes: "", méthodes: "*", SupportsCredentials = true )]
Je devais activer SupportsCredentials = true. Les origines, les en-têtes et les méthodes sont tous définis sur "*"