Ce que j'essaie de faire
J'ai une API Web Web principale ASP.Net principale hébergée sur un plan Azure Free (code source: https://github.com/killerrin/Portfolio-Backend ).
J'ai aussi un site Web client que je veux faire utiliser cette API. L'application cliente ne sera pas hébergée sur Azure, mais plutôt sur Github Pages ou sur un autre service d'hébergement Web auquel j'ai accès. Pour cette raison, les noms de domaine ne s'aligneront pas.
En regardant cela, je dois activer CORS du côté de l'API Web, mais j'ai tout essayé depuis plusieurs heures maintenant et cela refuse de fonctionner.
Comment j'ai la configuration du client C'est juste un simple client écrit en React.js. J'appelle les API par le biais de AJAX dans Jquery. Le site React fonctionne donc je sais que ce n'est pas ça. L'appel de l'API Jquery fonctionne comme je l'ai confirmé dans Attempt 1. Voici comment je passe les appels.
var apiUrl = "http://andrewgodfroyportfolioapi.azurewebsites.net/api/Authentication";
//alert(username + "|" + password + "|" + apiUrl);
$.ajax({
url: apiUrl,
type: "POST",
data: {
username: username,
password: password
},
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
var authenticatedUser = JSON.parse(response);
//alert("Data Loaded: " + authenticatedUser);
if (onComplete != null) {
onComplete(authenticatedUser);
}
},
error: function (xhr, status, error) {
//alert(xhr.responseText);
if (onComplete != null) {
onComplete(xhr.responseText);
}
}
});
Ce que j'ai essayé
Tentative 1 - La méthode 'appropriée'
https://docs.Microsoft.com/en-us/aspnet/core/security/cors
J'ai suivi ce didacticiel sur le site Web de Microsoft jusqu'à un T, en essayant les 3 options de l'activer globalement dans le fichier Startup.cs, de le configurer sur chaque contrôleur et de l'essayer pour chaque action.
En suivant cette méthode, le domaine croisé fonctionne, mais uniquement sur une seule action sur un seul contrôleur (POST au AccountController). Pour tout le reste, le middleware Microsoft.AspNetCore.Cors
refuse de définir les en-têtes.
J'ai installé Microsoft.AspNetCore.Cors
via NUGET et la version est 1.1.2
Voici comment je l'ai installé dans Startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add Cors
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
// Add framework services.
services.AddMvc();
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new CorsAuthorizationFilterFactory("MyPolicy"));
});
...
...
...
}
// This method gets called by the runtime. Use this method to configure
//the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
// Enable Cors
app.UseCors("MyPolicy");
//app.UseMvcWithDefaultRoute();
app.UseMvc();
...
...
...
}
Comme vous pouvez le constater, je fais tout ce qui est dit. J'ajoute Cors avant MVC les deux fois, et lorsque cela ne fonctionnait pas, j'ai essayé de mettre [EnableCors("MyPolicy")]
sur chaque contrôleur de la même manière
[Route("api/[controller]")]
[EnableCors("MyPolicy")]
public class AdminController : Controller
Tentative 2 - Brute le forçant
https://andrewlock.net/adding-default-security-headers-in-asp-net-core/
Après plusieurs heures d’essais sur la tentative précédente, j’ai pensé que j’essayerais de la forcer brutalement en essayant de définir les en-têtes manuellement, en les forçant à être exécutées à chaque réponse. Je l’ai fait après ce tutoriel sur la façon d’ajouter manuellement des en-têtes à chaque réponse.
Ce sont les en-têtes que j'ai ajoutés
.AddCustomHeader("Access-Control-Allow-Origin", "*")
.AddCustomHeader("Access-Control-Allow-Methods", "*")
.AddCustomHeader("Access-Control-Allow-Headers", "*")
.AddCustomHeader("Access-Control-Max-Age", "86400")
Ce sont d'autres en-têtes que j'ai essayés et qui ont échoué
.AddCustomHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE")
.AddCustomHeader("Access-Control-Allow-Headers", "content-type, accept, X-PINGOTHER")
.AddCustomHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Host, User-Agent, Accept, Accept: application/json, application/json, Accept-Language, Accept-Encoding, Access-Control-Request-Method, Access-Control-Request-Headers, Origin, Connection, Content-Type, Content-Type: application/json, Authorization, Connection, Origin, Referer")
Avec cette méthode, les en-têtes Cross Site sont correctement appliqués et s'affichent dans la console de développement et dans Postman. Le problème cependant, c'est que même s'il réussit le test Access-Control-Allow-Origin
, le navigateur Web jette un blason sur (je crois) Access-Control-Allow-Headers
en indiquant 415 (Unsupported Media Type)
Donc, la méthode de la force brute ne fonctionne pas non plus
Enfin
Quelqu'un a-t-il réussi à faire fonctionner cela et pourrait-il me donner un coup de main ou simplement me diriger dans la bonne direction?
EDIT
Donc, pour que les appels d'API puissent passer, j'ai dû arrêter d'utiliser JQuery et passer au format Pure Javascript XMLHttpRequest
name__.
Tentative 1
J'ai réussi à faire fonctionner le Microsoft.AspNetCore.Cors
en suivant la réponse de MindingData, sauf dans la méthode Configure
name__, qui place le app.UseCors
avant le app.UseMvc
.
En outre, une fois mélangé avec la solution API Javascript options.AllowAnyOrigin()
pour la prise en charge des caractères génériques, il a également commencé à fonctionner.
Tentative 2
J'ai donc réussi à faire fonctionner Attempt 2 (brute forcing) ... à la seule exception que le caractère générique pour Access-Control-Allow-Origin
ne fonctionne pas et que je dois donc définir manuellement les domaines qui y ont accès.
Ce n’est évidemment pas idéal car je souhaite simplement que cette WebAPI soit largement ouverte à tout le monde, mais cela fonctionne au moins pour moi sur un site séparé, ce qui signifie que c’est un début
app.UseSecurityHeadersMiddleware(new SecurityHeadersBuilder()
.AddDefaultSecurePolicy()
.AddCustomHeader("Access-Control-Allow-Origin", "http://localhost:3000")
.AddCustomHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, PATCH, DELETE")
.AddCustomHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type, Authorization"));
Comme vous avez une politique CORS très simple (autoriser toutes les demandes du domaine XXX), vous n'avez pas besoin de compliquer les choses. Essayez d’abord de procéder comme suit (une implémentation très basique de CORS).
Si vous ne l'avez pas déjà fait, installez le paquet Nuget CORS.
Install-Package Microsoft.AspNetCore.Cors
Dans la méthode ConfigureServices de votre startup.cs, ajoutez les services CORS.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(); // Make sure you call this previous to AddMvc
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Ensuite, dans votre méthode Configure de votre startup.cs, ajoutez ce qui suit:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Make sure you call this before calling app.UseMvc()
app.UseCors(
options => options.WithOrigins("http://example.com").AllowAnyMethod()
);
app.UseMvc();
}
Maintenant, essayez-le. Les stratégies sont pour quand vous voulez différentes stratégies pour différentes actions (par exemple différents hôtes ou différents en-têtes). Pour votre exemple simple, vous n'en avez vraiment pas besoin. Commencez avec cet exemple simple et Tweak comme vous devez à partir de là.
Lectures supplémentaires: http://dotnetcoretutorials.com/2017/01/03/enabling-cors-asp-net-core/
services.AddCors();
AVANT services.AddMvc (); Ajouter des UseCors dans Configurez
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
app.UseMvc();
Le point principal est que ajoutez app.UseCors
, avant app.UseMvc()
.
Assurez-vous de déclarer la fonctionnalité CORS avant MVC afin que le middleware se déclenche avant que le pipeline MVC ne prenne le contrôle et met fin à la demande.
Une fois que la méthode ci-dessus fonctionne, vous pouvez modifier la configuration d'une origine spécifique pour qu'elle accepte les appels d'API et évite de laisser votre API si ouverte à quiconque.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("ApiCorsPolicy", builder =>
{
builder.WithOrigins("http://localhost:4200").AllowAnyMethod().AllowAnyHeader();
}));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Dans la méthode configure, indiquez à CORS d'utiliser la stratégie que vous venez de créer:
app.UseCors("ApiCorsPolicy");
app.UseMvc();
Je viens de trouver cet article compact sur le sujet - https://dzone.com/articles/cors-in-net-core-net-core-security-part-vi
J'ai créé ma propre classe de middleware qui a fonctionné pour moi. Je pense que quelque chose ne va pas avec la classe de middleware .net core
public class CorsMiddleware
{
private readonly RequestDelegate _next;
public CorsMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext httpContext)
{
httpContext.Response.Headers.Add("Access-Control-Allow-Origin", "*");
httpContext.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
httpContext.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
httpContext.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CorsMiddlewareExtensions
{
public static IApplicationBuilder UseCorsMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CorsMiddleware>();
}
}
et utilisé de cette façon dans le startup.cs
app.UseCorsMiddleware();
Dans mon cas, seule la requête get
fonctionne bien selon la réponse de MindingData. Pour les autres types de demande, vous devez écrire:
app.UseCors(corsPolicyBuilder =>
corsPolicyBuilder.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
);
N'oubliez pas d'ajouter .AllowAnyHeader()
Pour développer ser8266077 's answer , j'ai toujours besoin de fournir une réponse OPTIONS pour demandes de contrôle en amont dans .NET Core 2.1-preview for mon cas d'utilisation:
// https://stackoverflow.com/a/45844400
public class CorsMiddleware
{
private readonly RequestDelegate _next;
public CorsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
context.Response.Headers.Add("Access-Control-Allow-Credentials", "true");
// Added "Accept-Encoding" to this list
context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Accept-Encoding, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name");
context.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS");
// New Code Starts here
if (context.Request.Method == "OPTIONS")
{
context.Response.StatusCode = (int)HttpStatusCode.OK;
await context.Response.WriteAsync(string.Empty);
}
// New Code Ends here
await _next(context);
}
}
puis activé le middleware comme dans Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware(typeof(CorsMiddleware));
// ... other middleware inclusion such as ErrorHandling, Caching, etc
app.UseMvc();
}
Aucune des procédures ci-dessus n'a aidé et j'ai alors lu article qui résolvait le problème.
Ci-dessous le code.
public void ConfigureServices(IServiceCollection services)
{
// Add service and create Policy with options
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials() );
});
services.AddMvc();
}
et
public void Configure(IApplicationBuilder app)
{
// ...
// global policy - assign here or on each controller
app.UseCors("CorsPolicy");
et au sommet de ma méthode d'action
[EnableCors("CorsPolicy")]
essayez d'ajouter jQuery.support.cors = true;
avant l'appel Ajax
Il se peut également que les données que vous envoyez à l’API soient erronées,
essayez d'ajouter la fonction JSON suivante
var JSON = JSON || {};
// implement JSON.stringify serialization
JSON.stringify = JSON.stringify || function (obj) {
var t = typeof (obj);
if (t != "object" || obj === null) {
// simple data type
if (t == "string") obj = '"' + obj + '"';
return String(obj);
}
else {
// recurse array or object
var n, v, json = [], arr = (obj && obj.constructor == Array);
for (n in obj) {
v = obj[n]; t = typeof (v);
if (t == "string") v = '"' + v + '"';
else if (t == "object" && v !== null) v = JSON.stringify(v);
json.Push((arr ? "" : '"' + n + '":') + String(v));
}
return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
}
};
// implement JSON.parse de-serialization
JSON.parse = JSON.parse || function (str) {
if (str === "") str = '""';
eval("var p=" + str + ";");
return p;
};
puis dans vos données: objet le changer en
data: JSON.stringify({
username: username,
password: password
}),
D'après votre commentaire dans la réponse de MindingData, cela n'a rien à voir avec votre CORS, cela fonctionne bien.
Votre action du contrôleur renvoie les mauvaises données. HttpCode 415 signifie "Type de support non supporté". Cela se produit lorsque vous transmettez le mauvais format au contrôleur (XML, par exemple, à un contrôleur qui accepte uniquement JSON) ou lorsque vous renvoyez un type incorrect (renvoyez Xml dans un contrôleur déclaré pour ne renvoyer que xml).
Pour plus tard, vérifiez l'existence de [Produces("...")]
attribute sur votre action
Dans launchSettings.json, sous iisSettings, définissez anonymousAuthentication sur true:
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:4200/",
"sslPort": 0
}
}
Ensuite, dans Startup.cs, sous ConfigureServices, avant services.AddMvc, ajoutez:
services.AddCors(options => options.AddPolicy("ApiCorsPolicy", builder =>
{
builder
.AllowAnyOrigin()
.WithHeaders(HeaderNames.AccessControlAllowHeaders, "Content-Type")
.AllowAnyMethod()
.AllowCredentials();
}));
puis dans la méthode configure, avant app.UseMvc (), ajoutez:
app.UseCors("ApiCorsPolicy");
Je pense que si vous utilisez votre propre middleware CORS, vous devez vous assurer qu'il s'agit bien de la requête CORS en vérifiant Origine en-tête.
public class CorsMiddleware
{
private readonly RequestDelegate _next;
private readonly IMemoryCache _cache;
private readonly ILogger<CorsMiddleware> _logger;
public CorsMiddleware(RequestDelegate next, IMemoryCache cache, ILogger<CorsMiddleware> logger)
{
_next = next;
_cache = cache;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context, IAdministrationApi adminApi)
{
if (context.Request.Headers.ContainsKey(CorsConstants.Origin) || context.Request.Headers.ContainsKey("Origin"))
{
if (!context.Request.Headers.TryGetValue(CorsConstants.Origin, out var Origin))
{
context.Request.Headers.TryGetValue("Origin", out Origin);
}
bool isAllowed;
// Getting Origin from DB to check with one from request and save it in cache
var result = _cache.GetOrCreateAsync(Origin, async cacheEntry => await adminApi.DoesExistAsync(Origin));
isAllowed = result.Result.Result;
if (isAllowed)
{
context.Response.Headers.Add(CorsConstants.AccessControlAllowOrigin, Origin);
context.Response.Headers.Add(
CorsConstants.AccessControlAllowHeaders,
$"{HeaderNames.Authorization}, {HeaderNames.ContentType}, {HeaderNames.AcceptLanguage}, {HeaderNames.Accept}");
context.Response.Headers.Add(CorsConstants.AccessControlAllowMethods, "POST, GET, PUT, PATCH, DELETE, OPTIONS");
if (context.Request.Method == "OPTIONS")
{
_logger.LogInformation("CORS with Origin {Origin} was handled successfully", Origin);
context.Response.StatusCode = (int)HttpStatusCode.NoContent;
return;
}
await _next(context);
}
else
{
if (context.Request.Method == "OPTIONS")
{
_logger.LogInformation("Preflight CORS request with Origin {Origin} was declined", Origin);
context.Response.StatusCode = (int)HttpStatusCode.NoContent;
return;
}
_logger.LogInformation("Simple CORS request with Origin {Origin} was declined", Origin);
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return;
}
}
await _next(context);
}
Pour moi, cela n'avait rien à voir avec le code que j'utilisais. Pour Azure, nous devions entrer dans les paramètres de App Service, dans le menu latéral, l'entrée "CORS". Là je devais ajouter le domaine que je demandais des choses. Une fois que j'ai eu ça, tout était magique.
La solution la plus simple est d'ajouter
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors(options => options.AllowAnyOrigin());
app.UseHttpsRedirection();
app.UseMvc();
}
à Startup.cs.
La réponse ci-dessus de MindingData fonctionnait bien, mais je devais utiliser Microsoft.AspNet.Cors au lieu de Microsoft.AspNetCore.Cors. J'utilise un projet d'API d'application Web .NetCore dans Visual Studio 2019