webentwicklung-frage-antwort-db.com.de

Erstellen eines Proxy für eine andere Web-API mit Asp.net-Kern

Ich entwickle eine ASP.Net Core-Webanwendung, in der ich eine Art "Authentifizierungs-Proxy" für einen anderen (externen) Webdienst erstellen muss.

Mit Authentifizierungs-Proxy meine ich, dass ich Anfragen über einen bestimmten Pfad meiner Web-App erhalten und die Header dieser Anfragen auf ein Authentifizierungs-Token überprüfen muss, das ich zuvor ausgegeben habe, und dann alle Anfragen mit dem gleiche Anforderungszeichenfolge/-inhalt an eine externe Web-API, mit der sich meine App über HTTP Basic authentifiziert.

Hier ist der ganze Prozess in Pseudo-Code

  • Der Client fordert ein Token an, indem er POST an eine eindeutige URL anpasst, die ich ihm zuvor gesendet habe
  • Meine App sendet ihm ein einzigartiges Token als Antwort auf diesen POST
  • Der Client sendet eine GET-Anforderung an eine bestimmte URL meiner App, beispielsweise /extapi, und fügt das Auth-Token im HTTP-Header hinzu
  • Meine App erhält die Anfrage und prüft, ob das Auth-Token vorhanden und gültig ist
  • Meine App stellt dieselbe Anforderung an die externe Web-API und authentifiziert die Anforderung mithilfe der BASIC-Authentifizierung
  • Meine App erhält das Ergebnis der Anfrage und sendet es an den Client zurück

Hier ist was ich jetzt habe. Es scheint gut zu funktionieren, aber ich frage mich, ob dies wirklich der Fall ist oder ob es keine elegantere oder bessere Lösung dafür gibt. Könnte diese Lösung langfristig Probleme beim Skalieren der Anwendung verursachen?

[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 ist eine HttpClient-Klasse, die an einer anderen Stelle instanziiert wird und ein Singleton ist und eine BaseAddressof http://someexternalapp.com/api/

Gibt es auch einen einfacheren Ansatz für die Tokenerstellung/Tokenprüfung als manuell durchzuführen?

23
Gimly

Am Ende implementierte ich eine Proxy-Middleware, die von einem Projekt in GitHub von Asp.Net inspiriert wurde.

Es implementiert im Wesentlichen eine Middleware, die die erhaltene Anfrage liest, eine Kopie davon erstellt und an einen konfigurierten Dienst zurücksendet, die Antwort des Dienstes liest und an den Anrufer zurücksendet.

12
Gimly

Wenn jemand interessiert ist, habe ich den Microsoft.AspNetCore.Proxy-Code genommen und es mit Middleware etwas verbessert.

Überprüfen Sie es hier: https://github.com/twitchax/AspNetCore.Proxy . NuGet hier: https://www.nuget.org/packages/AspNetCore.Proxy/ . Microsoft hat den anderen in diesem Beitrag erwähnten archiviert, und ich habe vor, auf Probleme in diesem Projekt zu reagieren.

Im Wesentlichen wird Reverse-Proxying für einen anderen Webserver wesentlich einfacher, da Sie Attribute für Methoden verwenden können, die eine Route mit args verwenden und die Proxy-Adresse berechnen.

[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

Um auf die Antwort von James Lawruk zurückzugreifen https://stackoverflow.com/a/54149906/6596451 Um das twitchax-Proxy-Attribut zum Laufen zu bringen, erhielt ich auch einen 404-Fehler, bis ich im ProxyRoute-Attribut die vollständige Route angegeben habe . Ich hatte meine statische Route in einem separaten Controller und der relative Pfad von der Controller-Route funktionierte nicht.

Das hat funktioniert: 

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

Das tut nicht:

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

Hoffe das hilft jemandem!

2

Hier ist eine grundlegende Implementierung der Proxy-Bibliothek für ASP.NET Core :

Dies implementiert die Autorisierung nicht, kann jedoch für jemanden nützlich sein, der einen einfachen Reverse-Proxy mit ASP.NET Core sucht. Wir verwenden dies nur für Entwicklungsphasen.

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

Ich hatte Glück, das AspNetCore.Proxy NuGet-Paket von twitchax zu verwenden , konnte es aber nicht mit der ProxyRoute-Methode erreichen, die in twitchaxs Antwort gezeigt wird. (Könnte leicht ein Fehler an meinem Ende gewesen sein.) 

Stattdessen habe ich die Zuordnung in Statup.cs Configure () -Methode ähnlich dem folgenden Code definiert.

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

Eine Implementierung der Nice Reverse Proxy Middleware finden Sie auch hier: https://auth0.com/blog/building-a-reverse-proxy-in-dot-net-core/

Beachten Sie, dass ich diese Zeile hier ersetzt habe

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

mit

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

Originalheader (z. B. ein Autorisierungsheader mit einem Trägertoken) werden ohne meine Modifikation in meinem Fall nicht hinzugefügt.

0
baumgarb