web-dev-qa-db-fra.com

Création d'un proxy vers une autre API Web avec le noyau Asp.net

Je développe une application Web ASP.Net Core dans laquelle je dois créer une sorte de "proxy d'authentification" pour un autre service Web (externe).

Ce que je veux dire par proxy d'authentification, c'est que je recevrai des demandes via un chemin spécifique de mon application Web et que je devrai vérifier les en-têtes de ces demandes pour un jeton d'authentification que j'aurai émis précédemment, puis rediriger toutes les demandes avec le même chaîne de requête/contenu à une API Web externe avec laquelle mon application s'authentifiera via HTTP Basic auth.

Voici le processus complet en pseudo-code

  • Le client demande un jeton en créant un POST vers une URL unique que je lui ai déjà envoyée
  • Mon application lui envoie un jeton unique en réponse à cet envoi
  • Le client envoie une requête GET à une URL spécifique de mon application, par exemple /extapi et ajoute le jeton d'authentification dans l'en-tête HTTP.
  • Mon application reçoit la demande, vérifie que le jeton d'authentification est présent et valide.
  • Mon application envoie la même demande à l'API Web externe et authentifie la demande à l'aide de l'authentification BASIC.
  • Mon application reçoit le résultat de la demande et le renvoie au client.

Voici ce que j'ai pour l'instant. Cela semble bien fonctionner, mais je me demande si c'est vraiment comme cela que cela devrait être fait ou s'il n'y a pas de solution plus élégante ou meilleure à cela. Cette solution pourrait-elle créer des problèmes à long terme pour la mise à l'échelle de l'application?

[HttpGet]
public async Task GetStatement()
{
    //TODO check for token presence and reject if issue

    var queryString = Request.QueryString;
    var response = await _httpClient.GetAsync(queryString.Value);
    var content = await response.Content.ReadAsStringAsync();

    Response.StatusCode = (int)response.StatusCode;
    Response.ContentType = response.Content.Headers.ContentType.ToString();
    Response.ContentLength = response.Content.Headers.ContentLength;

    await Response.WriteAsync(content);
}

[HttpPost]
public async Task PostStatement()
{
    using (var streamContent = new StreamContent(Request.Body))
    {
        //TODO check for token presence and reject if issue

        var response = await _httpClient.PostAsync(string.Empty, streamContent);
        var content = await response.Content.ReadAsStringAsync();

        Response.StatusCode = (int)response.StatusCode;

        Response.ContentType = response.Content.Headers.ContentType?.ToString();
        Response.ContentLength = response.Content.Headers.ContentLength;

        await Response.WriteAsync(content);
    }
}

_httpClient étant une classe HttpClient instanciée ailleurs et un singleton avec une BaseAddressde http://someexternalapp.com/api/

En outre, existe-t-il une approche plus simple pour la création de jeton/la vérification de jeton que de le faire manuellement?

23
Gimly

J'ai fini par implémenter un middleware proxy inspiré de un projet de GitHub d'Asp.Net .

Il implémente essentiellement un middleware qui lit la requête reçue, en crée une copie et la renvoie à un service configuré, lit la réponse du service et la renvoie à l'appelant.

12
Gimly

Si quelqu'un est intéressé, j'ai pris le code Microsoft.AspNetCore.Proxy et je l'ai amélioré avec le middleware.

Découvrez-le ici: https://github.com/twitchax/AspNetCore.Proxy . NuGet ici: https://www.nuget.org/packages/AspNetCore.Proxy/ . Microsoft a archivé l’autre mentionné dans ce message et j’ai l’intention de répondre à tout problème concernant ce projet.

Fondamentalement, cela facilite beaucoup la procuration inverse sur un autre serveur Web en vous permettant d'utiliser des attributs pour les méthodes qui prennent une route avec args et calculent l'adresse proxy.

[ProxyRoute("api/searchgoogle/{query}")]
public static Task<string> SearchGoogleProxy(string query)
{
    // Get the proxied address.
    return Task.FromResult($"https://www.google.com/search?q={query}");
}
3
twitchax

Piggy-backing sur la réponse de James Lawruk https://stackoverflow.com/a/54149906/6596451 pour que l'attribut proxy twitchax fonctionne, j'ai également une erreur 404 jusqu'à ce que je spécifie l'itinéraire complet dans l'attribut ProxyRoute . J'avais mon itinéraire statique dans un contrôleur séparé et le chemin relatif de l'itinéraire du contrôleur ne fonctionnait pas.

Cela a fonctionné: 

public class ProxyController : Controller
{
    [ProxyRoute("api/Proxy/{name}")]
    public static Task<string> Get(string name)
    {
        return Task.FromResult($"http://www.google.com/");
    }
}

Cela ne veut pas:

[Route("api/[controller]")]
public class ProxyController : Controller
{
    [ProxyRoute("{name}")]
    public static Task<string> Get(string name)
    {
        return Task.FromResult($"http://www.google.com/");
    }
}

J'espère que cela aide quelqu'un!

2
Daniel Plainview

Voici une implémentation de base de Bibliothèque de proxy pour ASP.NET Core :

Cela n'implémente pas l'autorisation mais pourrait être utile à quelqu'un qui recherche un proxy inverse simple avec ASP.NET Core. Nous ne l'utilisons que pour les étapes de développement.

using System;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;

namespace Sample.Proxy
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddLogging(options =>
            {
                options.AddDebug();
                options.AddConsole(console =>
                {
                    console.IncludeScopes = true;
                });
            });

            services.AddProxy(options =>
            {
                options.MessageHandler = new HttpClientHandler
                {
                    AllowAutoRedirect = false,
                    UseCookies = true 
                };

                options.PrepareRequest = (originalRequest, message) =>
                {
                    var Host = GetHeaderValue(originalRequest, "X-Forwarded-Host") ?? originalRequest.Host.Host;
                    var port = GetHeaderValue(originalRequest, "X-Forwarded-Port") ?? originalRequest.Host.Port.Value.ToString(CultureInfo.InvariantCulture);
                    var prefix = GetHeaderValue(originalRequest, "X-Forwarded-Prefix") ?? originalRequest.PathBase;

                    message.Headers.Add("X-Forwarded-Host", Host);
                    if (!string.IsNullOrWhiteSpace(port)) message.Headers.Add("X-Forwarded-Port", port);
                    if (!string.IsNullOrWhiteSpace(prefix)) message.Headers.Add("X-Forwarded-Prefix", prefix);

                    return Task.FromResult(0);
                };
            });
        }

        private static string GetHeaderValue(HttpRequest request, string headerName)
        {
            return request.Headers.TryGetValue(headerName, out StringValues list) ? list.FirstOrDefault() : null;
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseWebSockets()
                .Map("/api", api => api.RunProxy(new Uri("http://localhost:8833")))
                .Map("/image", api => api.RunProxy(new Uri("http://localhost:8844")))
                .Map("/admin", api => api.RunProxy(new Uri("http://localhost:8822")))
                .RunProxy(new Uri("http://localhost:8811"));
        }

        public static void Main(string[] args)
        {
            var Host = new WebHostBuilder()
                .UseKestrel()
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            Host.Run();
        }
    }
}
1
Kerem Demirer

J'ai eu de la chance en utilisant le paquet AspNetCore.Proxy NuGet de twitchax , mais je n'ai pas réussi à le faire fonctionner avec la méthode ProxyRoute indiquée dans la réponse de twitchax . (Cela aurait facilement pu être une erreur de ma part.) 

Au lieu de cela, j'ai défini le mappage dans la méthode Statup.cs Configure () similaire au code ci-dessous.

app.UseProxy("api/someexternalapp-proxy/{arg1}", async (args) =>
{
    string url = "https://someexternalapp.com/" + args["arg1"];
    return await Task.FromResult<string>(url);
});
0
James Lawruk

Une implémentation de middleware de proxy inverse Nice peut également être trouvée ici: https://auth0.com/blog/building-a-reverse-proxy-in-dot-net-core/

Notez que j'ai remplacé cette ligne ici

requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());

avec

requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToString());

Les en-têtes d'origine (par exemple, un en-tête d'autorisation avec un jeton porteur) ne seraient pas ajoutés sans ma modification dans mon cas.

0
baumgarb