HttpListener-Zugriff verweigert

179

Ich schreibe einen HTTP-Server in C #.

Wenn ich versuche, die Funktion auszuführen, HttpListener.Start()bekomme ich ein HttpListenerExceptionSprichwort

"Zugriff abgelehnt".

Wenn ich die App im Admin-Modus unter Windows 7 starte, funktioniert sie einwandfrei.

Kann ich es ohne Admin-Modus laufen lassen? wenn ja wie? Wenn nicht, wie kann ich die App nach dem Start in den Admin-Modus ändern?

using System;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        private HttpListener httpListener = null;

        static void Main(string[] args)
        {
            Program p = new Program();
            p.Server();
        }

        public void Server()
        {
            this.httpListener = new HttpListener();

            if (httpListener.IsListening)
                throw new InvalidOperationException("Server is currently running.");

            httpListener.Prefixes.Clear();
            httpListener.Prefixes.Add("http://*:4444/");

            try
            {
                httpListener.Start(); //Throws Exception
            }
            catch (HttpListenerException ex)
            {
                if (ex.Message.Contains("Access is denied"))
                {
                    return;
                }
                else
                {
                    throw;
                }
            }
        }
    }
}
Randall Flagg
quelle
Wenn jemand diesen Fehler vermeiden möchte, kann er versuchen, ihn mit TcpListener zu schreiben. Es erfordert keine Administratorrechte
Vlad
Ich habe das gleiche Problem: In Visual Studio 2008 + Windows 7 wird der Fehler "Zugriff verweigert" angezeigt. Um dem entgegenzuwirken, müssen Sie Visual Studio 2008 im Administratormodus ausführen
Mark Khor,

Antworten:

307

Ja, Sie können HttpListener im Nicht-Administratormodus ausführen. Sie müssen lediglich Berechtigungen für die jeweilige URL erteilen. z.B

netsh http add urlacl url=http://+:80/MyUri user=DOMAIN\user

Die Dokumentation ist hier .

Darrel Miller
quelle
48
Dies ist hilfreich, aber der Vollständigkeit httpListener.Prefixes.Add("http://*:4444/");halber muss die in dieser Codezeile angegebene URL genau mit der im netshBefehl angegebenen übereinstimmen . Zum Beispiel hatte ich httpListener.Prefixes.Add("http://127.0.0.1:80/");den gleichen netshBefehl wie Sie, und die HttpListenerException wird weiterhin ausgelöst. Ich musste mich ändern httpListener.Prefixes.Add("http://+:80/");Danke für deine Hilfe @Darrel Miller, weil du mich auf den richtigen Weg gebracht hast, um das herauszufinden!
Psyklopz
10
Und vergessen Sie nicht den abschließenden Schrägstrich, wenn "MyUri" leer ist, sonst wird eine The parameter is incorrectFehlermeldung angezeigt . Beispiel:url=http://+:80/
Igor Brejc
14
Gibt es eine Möglichkeit, dies für einen nicht administrativen Benutzer zu tun, auch für http://localhost:80/? Ich habe eine Desktop-Anwendung, die eine Anfrage unter einer solchen URL erhalten muss, und es scheint eine Schande zu sein, dass ein Administrator sie auf 50 Desktops installiert, nur für diesen einen Zweck.
John Saunders
5
Scheinbar nicht. Auf der Dokumentationsseite wird auch nicht erwähnt, dass eine Erhöhung oder Administratorrechte erforderlich sind. Es ist also leicht anzunehmen, dass dies als "intelligenterer" TCPListener fungiert, wenn es in Wirklichkeit einen GROSSEN Grund gibt, ihn nicht zu verwenden - dies ist jedoch nicht dokumentiert.
David Ford
4
Das Ausführen von netsh erfordert admin; (
superfly71
27

Kann ich es ohne Admin-Modus laufen lassen? wenn ja wie? Wenn nicht, wie kann ich die App nach dem Start in den Admin-Modus ändern?

Sie können nicht, es muss mit erhöhten Berechtigungen beginnen. Sie können es mit dem runasVerb neu starten , wodurch der Benutzer aufgefordert wird, in den Administratormodus zu wechseln

static void RestartAsAdmin()
{
    var startInfo = new ProcessStartInfo("yourApp.exe") { Verb = "runas" };
    Process.Start(startInfo);
    Environment.Exit(0);
}

EDIT: Eigentlich stimmt das nicht; HttpListener kann ohne erhöhte Berechtigungen ausgeführt werden, Sie müssen jedoch die Berechtigung für die URL erteilen, die Sie abhören möchten. Siehe Darrel Millers Antwort für Details.

Thomas Levesque
quelle
Können Sie bitte erklären, warum ich mit erhöhten Rechten beginnen muss?
Randall Flagg
Sie müssen auch diese Zeile 'startInfo.UseShellExecute = false;' hinzufügen. vor 'Process.Start (startInfo);'
Randall Flagg
@ Randall: Weil Windows so funktioniert ... ein Prozess kann nicht in den Admin-Modus wechseln, während er ausgeführt wird. In Bezug auf UseShellExecute: Dies hängt davon ab, was Sie ausführen. Ich habe meinen Code mit "notepad.exe" getestet. Ohne UseShellExecute = false funktioniert er einwandfrei.
Thomas Levesque
Vielen Dank. Informationen zu UseShellExecute: Ich habe versucht, den von mir veröffentlichten Code auszuführen. Ein weiteres Problem ist, dass ich aus irgendeinem Grund einmal gefragt wurde, ob ich als Administrator ausgeführt werden möchte, und zu einem anderen Zeitpunkt danach nicht mehr gefragt wird. Ich habe neu gestartet, Debugged, um sicherzustellen, dass es dort und nichts geht. irgendwelche Vorschläge?
Randall Flagg
1
@Thomas Es gibt kein Problem, HttpListener im Nicht-Administrator-Modus auszuführen.
Darrel Miller
23

Wenn Sie http://localhost:80/als Präfix verwenden, können Sie http-Anforderungen abhören, ohne dass Administratorrechte erforderlich sind.

Ehsan Mirsaeedi
quelle
2
Können Sie einen Beispielcode posten? Ich habe es gerade mit versucht http://localhost:80/und einen "Zugriff verweigert" erhalten.
John Saunders
2
Entschuldigung, das scheint nicht zu funktionieren. Bitte überprüfen Sie, ob Sie als Administrator ausgeführt werden. Dies sollte nicht der Normalfall sein.
Jonno
Wenn dies funktionieren würde, würde ich es als Windows-Fehler betrachten. Egal, was Sie über Windows denken, ich gehe davon aus, dass dies ein sehr grundlegendes Niveau ist, das sie sehr, sehr wahrscheinlich nicht übersehen haben, richtig zu handhaben.
Hoijui
26
Mehr als 2 Jahre später funktioniert dies für mich jetzt unter Windows Server 2008 R2 mit .NET Framework 4.5. httpListener.Prefixes.Add("http://*:4444/");zeigt zwar einen Access DeniedFehler, httpListener.Prefixes.Add("http://localhost:4444/");funktioniert aber ohne Probleme. Microsoft scheint localhostvon diesen Einschränkungen ausgeschlossen zu sein. Interessanterweise wird httpListener.Prefixes.Add("http://127.0.0.1:4444/");immer noch ein Fehler "Zugriff verweigert" angezeigt. Das einzige, was funktioniert, istlocalhost:{any port}
Tony,
1
@ Tony danke dein Kommentar war eigentlich der wertvollste für mich. Ich hatte "127.0.0.1" in mehreren Konfigurationen. QA berichtete, dass es unter Windows 8.1 nicht funktionierte, aber in Windows 10 einwandfrei war (und ich habe es selbst in Win10 getestet). Seltsamerweise scheint es Microsoft allen erlaubt zu haben, 127.0.0.1 in Win10 zu verwenden, aber nicht 8.1 (und vermutlich frühere Versionen), aber sicher ist localhost in Ordnung. Vielen Dank
Adam Plocher
20

Die Syntax war für mich falsch, Sie müssen die Anführungszeichen einfügen:

netsh http add urlacl url="http://+:4200/" user=everyone

ansonsten habe ich "Der Parameter ist falsch" erhalten

Dave
quelle
4
"Der Parameter ist falsch" kann auch zurückgegeben werden, wenn Sie kein Trailing /in der URL angeben
LostSalad
13

Wenn Sie das Flag "user = Everyone" verwenden möchten, müssen Sie es an Ihre Systemsprache anpassen. In Englisch ist es wie erwähnt:

netsh http add urlacl url=http://+:80/ user=Everyone

Auf Deutsch wäre es:

netsh http add urlacl url=http://+:80/ user=Jeder
Christoph Brückmann
quelle
1
In spanischsprachigen Systemen tun Sie: user = Todos
Rodrigo Rodrigues
Außerdem musste ich die URL in Anführungszeichen setzen, wie in einer anderen Antwort erwähnt.
Carsten Führmann
10

Als Alternative, für die weder Elevation noch Netsh erforderlich sind, können Sie beispielsweise auch TcpListener verwenden.

Das Folgende ist ein modifizierter Auszug dieses Beispiels: https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthDesktopApp

// Generates state and PKCE values.
string state = randomDataBase64url(32);
string code_verifier = randomDataBase64url(32);
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
const string code_challenge_method = "S256";

// Creates a redirect URI using an available port on the loopback address.
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
output("redirect URI: " + redirectURI);

// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
    authorizationEndpoint,
    System.Uri.EscapeDataString(redirectURI),
    clientID,
    state,
    code_challenge,
    code_challenge_method);

// Opens request in the browser.
System.Diagnostics.Process.Start(authorizationRequest);

// Waits for the OAuth authorization response.
var client = await listener.AcceptTcpClientAsync();

// Read response.
var response = ReadString(client);

// Brings this app back to the foreground.
this.Activate();

// Sends an HTTP response to the browser.
WriteStringAsync(client, "<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please close this window and return to the app.</body></html>").ContinueWith(t =>
{
    client.Dispose();
    listener.Stop();

    Console.WriteLine("HTTP server stopped.");
});

// TODO: Check the response here to get the authorization code and verify the code challenge

Die Lese- und Schreibmethoden sind:

private string ReadString(TcpClient client)
{
    var readBuffer = new byte[client.ReceiveBufferSize];
    string fullServerReply = null;

    using (var inStream = new MemoryStream())
    {
        var stream = client.GetStream();

        while (stream.DataAvailable)
        {
            var numberOfBytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
            if (numberOfBytesRead <= 0)
                break;

            inStream.Write(readBuffer, 0, numberOfBytesRead);
        }

        fullServerReply = Encoding.UTF8.GetString(inStream.ToArray());
    }

    return fullServerReply;
}

private Task WriteStringAsync(TcpClient client, string str)
{
    return Task.Run(() =>
    {
        using (var writer = new StreamWriter(client.GetStream(), new UTF8Encoding(false)))
        {
            writer.Write("HTTP/1.0 200 OK");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Type: text/html; charset=UTF-8");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Length: " + str.Length);
            writer.Write(Environment.NewLine);
            writer.Write(Environment.NewLine);
            writer.Write(str);
        }
    });
}
Michael Olsen
quelle
Dies war sehr nützlich, da wir beim Implementieren eines IBrowser für die IdentityModel.OidcClient-Bibliothek das Problem HttpListener hatten, also einen sehr ähnlichen Anwendungsfall wie oben.
Mackie
1
Es ist jedoch erwähnenswert, dass der obige Code Fehler enthält. Erstens verursacht die Verwendung eines StreamWriter mit UTF8 in einigen Browsern Probleme. Ich vermute, dass eine Stückliste ausgegeben wird oder so etwas. Ich habe es in ASCII geändert und alles ist gut. Ich habe auch Code hinzugefügt, um darauf zu warten, dass Daten zum Lesen im Stream verfügbar werden, da manchmal keine Daten zurückgegeben werden.
Mackie
Danke @mackie - dieser Teil wurde direkt aus Microsofts eigenem Beispiel entnommen. Ich denke, sie haben es nicht richtig getestet. Wenn Sie meine Antwort bearbeiten können, korrigieren Sie die Methoden. Andernfalls senden Sie mir möglicherweise die Änderungen und ich kann sie aktualisieren.
Michael Olsen
2
Anstelle von ASCII sollte der folgende Konstruktor verwendet werden new UTF8Encoding(false), der das Ausgeben einer Stückliste deaktiviert. Ich habe dies bereits in der Antwort geändert
Sebastian
Ich bevorzuge diese Lösung gegenüber der akzeptierten Antwort. Netsh laufen zu lassen scheint ein Hack zu sein. Dies ist eine solide programmatische Lösung. Vielen Dank!
Jim Gomes
7

Sie können Ihre Anwendung als Administrator starten, wenn Sie Ihrem Projekt Anwendungsmanifest hinzufügen.

Fügen Sie Ihrem Projekt einfach ein neues Element hinzu und wählen Sie "Anwendungsmanifestdatei". Ändern Sie das <requestedExecutionLevel>Element in:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
R. Titov
quelle
7

Standardmäßig definiert Windows das folgende Präfix, das allen zur Verfügung steht: http: // +: 80 / Temporary_Listen_Addresses /

So können Sie Ihre HttpListenerVia registrieren :

Prefixes.Add("http://+:80/Temporary_Listen_Addresses/" + Guid.NewGuid().ToString("D") + "/";

Dies führt manchmal zu Problemen mit Software wie Skype, die standardmäßig versucht, Port 80 zu verwenden.

Sebastian
quelle
1
Skype war der Schuldige. Ich habe den Port auf nicht 80 geändert und es hat funktioniert.
Gehe zum
5
httpListener.Prefixes.Add("http://*:4444/");

Sie verwenden "*", um das folgende cmd als Administrator auszuführen

netsh http add urlacl url=http://*:4444/ user=username

no use +, muss * verwenden, da Sie * angeben: 4444 ~.

https://msdn.microsoft.com/en-us/library/system.net.httplistener.aspx

NamedStar
quelle
Das war es für mich. Es hat bei mir funktioniert.Add("http://+:9000/")
John Gietzen
2

Ich hatte auch ein ähnliches Problem. Wenn Sie bereits eine URL reserviert haben, müssen Sie zuerst die URL löschen, um sie im Nicht-Administrator-Modus auszuführen. Andernfalls schlägt sie mit dem Fehler "Zugriff verweigert" fehl.

netsh http delete urlacl url=http://+:80
vivek nuna
quelle