Accesso HttpListener negato


180

Sto scrivendo un server HTTP in C #.

Quando provo ad eseguire la funzione HttpListener.Start()ricevo un HttpListenerExceptiondetto

"Accesso negato".

Quando eseguo l'app in modalità amministratore in Windows 7, funziona perfettamente.

Posso farlo funzionare senza la modalità amministratore? se si come? In caso contrario, come posso fare in modo che l'app passi alla modalità amministratore dopo l'avvio?

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;
                }
            }
        }
    }
}

Se qualcuno vuole evitare quell'errore, può provare a scriverlo con TcpListener. Non richiede privilegi di amministratore
Vlad

Devo affrontare lo stesso problema, in Visual Studio 2008 + Windows 7, si verifica l'errore "Accesso negato", per risolvere il problema è eseguire Visual Studio 2008 in modalità Amministratore
Mark Khor,

Risposte:


307

Sì, puoi eseguire HttpListener in modalità non amministratore. Tutto quello che devi fare è concedere le autorizzazioni al particolare URL. per esempio

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

La documentazione è qui .


48
Questo è utile, ma per completezza, l'URL specificato in questa riga di codice: httpListener.Prefixes.Add("http://*:4444/");deve corrispondere ESATTAMENTE a quello nel netshcomando. Ad esempio, avevo httpListener.Prefixes.Add("http://127.0.0.1:80/");lo stesso netshcomando che hai tu e HttpListenerException verrà comunque lanciato. Ho dovuto cambiare httpListener.Prefixes.Add("http://+:80/");Grazie per il tuo aiuto @Darrel Miller, perché mi hai portato sulla strada giusta per capirlo!
psyklopz,

10
E non dimenticare la barra finale se "MyUri" è vuoto, altrimenti otterrai un The parameter is incorrecterrore. Esempio:url=http://+:80/
Igor Brejc,

14
C'è un modo per farlo per un utente non amministrativo, anche per http://localhost:80/? Ho un'applicazione desktop che deve ricevere una richiesta su tale URL e sembra un peccato richiedere che un amministratore la installi su 50 desktop, solo per questo scopo.
John Saunders,

5
Apparentemente no. Non c'è nemmeno menzione di privilegi amministrativi o di elevazione richiesti nella pagina della documentazione. Quindi è facile supporre che questo agirà come un TCPListener "più intelligente", quando in realtà c'è una GRANDE ragione per non usarlo - eppure questo non è documentato.
David Ford,

4
L'esecuzione di netsh richiede admin; (
superfly71

27

Posso farlo funzionare senza la modalità amministratore? se si come? In caso contrario, come posso fare in modo che l'app passi alla modalità amministratore dopo l'avvio?

Non puoi, deve iniziare con privilegi elevati. Puoi riavviarlo con il runasverbo, che chiederà all'utente di passare alla modalità amministratore

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

EDIT: in realtà, non è vero; HttpListener può essere eseguito senza privilegi elevati, ma è necessario autorizzare l'URL su cui si desidera ascoltare. Vedi la risposta di Darrel Miller per i dettagli.


Puoi spiegare perché devo iniziare con privilegi elevati?
Randall Flagg,

È inoltre necessario aggiungere questa riga 'startInfo.UseShellExecute = false;' prima di "Process.Start (startInfo);"
Randall Flagg,

@Randall: perché è così che funziona Windows ... un processo non può passare alla modalità amministratore mentre è in esecuzione. Per quanto riguarda UseShellExecute: dipende da cosa stai eseguendo. Ho testato il mio codice con "notepad.exe", funziona bene senza UseShellExecute = false
Thomas Levesque,

Grazie. Informazioni su UseShellExecute: ho provato a eseguire il codice che ho pubblicato. Un altro problema è che per qualche motivo mi ha chiesto una volta se volevo correre come amministratore e in qualsiasi altro momento successivo. Ho riavviato il debug per assicurarmi che vada lì e niente. eventuali suggerimenti?
Randall Flagg,

1
@Thomas Non ci sono problemi nell'esecuzione di HttpListener in modalità non amministratore.
Darrel Miller,

23

Se si utilizza http://localhost:80/come prefisso, è possibile ascoltare le richieste http senza i privilegi di amministratore.


2
Puoi pubblicare qualche codice di esempio? Ho appena provato questo http://localhost:80/e ho ottenuto un "Accesso negato".
John Saunders,

2
Spiacente, non sembra funzionare. Si prega di verificare se si esegue come amministratore, che non dovrebbe essere il caso normale.
Jonno,

se questo funzionasse, lo considererei un bug di Windows. non importa cosa ne pensi di Windows, presumo che questo sia di un livello molto semplice che molto, molto probabilmente, non hanno mancato di gestirlo correttamente.
hoijui,

26
Quindi, più di 2 anni dopo, questo funziona per me ora su Windows Server 2008 R2 con .NET Framework 4.5. httpListener.Prefixes.Add("http://*:4444/");infatti mostra un Access Deniederrore ma httpListener.Prefixes.Add("http://localhost:4444/");funziona senza alcun problema. Sembra che Microsoft sia esclusa localhostda queste restrizioni. È interessante notare httpListener.Prefixes.Add("http://127.0.0.1:4444/");che mostra ancora un errore di accesso negato, quindi l'unica cosa che funziona èlocalhost:{any port}
Tony

1
@Tony grazie il tuo commento è stato in realtà il più prezioso per me. Ho avuto "127.0.0.1" in diverse configurazioni. Il QA ha riferito che non funzionava su Windows 8.1 ma era su Windows 10 bene (e l'ho testato io stesso in Win10). Stranamente, sembra che Microsoft abbia concesso a Tutti di utilizzare 127.0.0.1 in Win10 ma non 8.1 (e presumibilmente versioni precedenti), ma abbastanza sicuro, localhost va bene. Grazie
Adam Plocher,

20

La sintassi era sbagliata per me, è necessario includere le virgolette:

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

altrimenti ho ricevuto "Il parametro non è corretto"


4
"Il parametro non è corretto" può anche essere restituito se non si specifica un finale /sull'URL
LostSalad

13

Nel caso in cui si desideri utilizzare il flag "user = Everyone", è necessario adattarlo alla lingua del proprio sistema. In inglese è come detto:

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

In tedesco sarebbe:

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

1
Nei sistemi in lingua spagnola, fai: user = Todos
Rodrigo Rodrigues,

Inoltre, dovevo inserire l'URL tra virgolette, come indicato in un'altra risposta.
Carsten Führmann,

10

In alternativa, che non richiede elevazione o netsh, puoi anche usare TcpListener per esempio.

Di seguito è riportato un estratto modificato di questo esempio: 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

I metodi di lettura e scrittura sono:

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);
        }
    });
}

Questo è stato davvero utile poiché abbiamo riscontrato il problema HttpListener durante l'implementazione di un IBrowser per la libreria IdentityModel.OidcClient, quindi un caso d'uso molto simile al precedente.
Mackie,

1
Vale la pena notare che il codice sopra contiene bug. Innanzitutto l'utilizzo di StreamWriter con UTF8 causa problemi in alcuni browser, immagino a causa dell'uscita di una distinta componenti o qualcosa del genere. L'ho cambiato in ASCII e tutto va bene. Ho anche aggiunto un po 'di codice per attendere che i dati diventassero disponibili per la lettura sullo stream in quanto a volte non restituirebbe alcun dato.
Mackie,

Grazie @mackie - quella parte è stata presa direttamente dal campione di Microsoft in realtà. Immagino che non lo abbiano davvero testato correttamente. Se riesci a modificare la mia risposta, vai avanti e correggi i metodi, altrimenti potresti inviarmi le modifiche e posso aggiornarlo.
Michael Olsen,

2
Invece di usare ASCII si dovrebbe usare il seguente costruttore new UTF8Encoding(false), che disabilita l'emissione di una DBA. L'ho già cambiato nella risposta
Sebastian,

Preferisco questa soluzione rispetto alla risposta accettata. L'esecuzione di netsh sembra un trucco. Questa è una solida soluzione programmatica. Grazie!
Jim Gomes,

7

È possibile avviare l'applicazione come amministratore se si aggiunge Application Manifest al progetto.

Basta aggiungere un nuovo elemento al progetto e selezionare "File manifest dell'applicazione". Cambia l' <requestedExecutionLevel>elemento in:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

7

Per impostazione predefinita, Windows definisce il seguente prefisso che è disponibile per tutti: http: // +: 80 / Temporary_Listen_Addresses /

Quindi puoi registrare il tuo HttpListenertramite:

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

Questo a volte causa problemi con software come Skype che tenterà di utilizzare la porta 80 per impostazione predefinita.


1
Skype era il colpevole. Ho cambiato la porta per non essere 80 e ha funzionato.
Vai


2

Ho anche riscontrato un problema simile: se hai già prenotato l'URL, devi prima eliminare l'URL per eseguirlo in modalità non admin altrimenti fallirà con Accesso è negato errore.

netsh http delete urlacl url=http://+:80
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.