2010-10-25 3 views
118

Sto scrivendo un server HTTP in C#.Accesso HttpListener negato

Quando provo ad eseguire la funzione HttpListener.Start() ottengo una HttpListenerException dicendo

"Accesso negato".

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

Posso farlo funzionare senza la modalità amministratore? se sì 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; 
       } 
      } 
     } 
    } 
} 
+0

Se qualcuno vuole evitare che l'errore si può provare a scrivere con TcpListener . Non richiede i privilegi di amministratore – Vlad

risposta

232

Sì, è possibile eseguire HttpListener in modalità non amministratore. Tutto quello che devi fare è concedere le autorizzazioni per l'URL specifico. per esempio.

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

La documentazione è here.

+33

Questo è utile, ma per completezza, l'URL specificato in questa riga di codice: 'httpListener.Prefixes.Add (" http: // *: 4444/");' deve corrispondere ESATTAMENTE a quello nel comando 'netsh' . Ad esempio, ho avuto 'httpListener.Prefixes.Add (" http://127.0.0.1:80/ ");' e lo stesso comando 'netsh' che hai, e l'HttpListenerException sarà comunque lanciata. Avevo bisogno di cambiare 'httpListener.Prefixes.Add (" http: // +: 80/");' Grazie per il tuo aiuto @Darrel Miller, perché mi hai messo sulla strada giusta per capirlo! – psyklopz

+6

E non dimenticare la barra finale se "MyUri" è vuoto, altrimenti otterrai un errore 'The parameter is incorrect'. Esempio: 'url = http: // +: 80 /' –

+11

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

25

Posso farlo funzionare senza modalità amministratore? se sì come? In caso contrario, come posso far passare l'app in modalità amministratore dopo l'avvio?

Non è possibile, deve iniziare con privilegi elevati. È possibile riavviare con il runas verb, che richiede 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à, questo non è vero; HttpListener può funzionare senza privilegi elevati, ma è necessario dare il permesso per l'URL su cui si desidera ascoltare. Vedi Darrel Miller's answer per i dettagli.

+0

Puoi spiegare perché devo iniziare con privilegi elevati? –

+0

È inoltre necessario aggiungere questa riga 'startInfo.UseShellExecute = false;' prima di 'Process.Start (startInfo);' –

+0

@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 senza UseShellExecute = false –

14

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

+0

Puoi pubblicare qualche codice di esempio? Ho appena provato questo con 'http: // localhost: 80 /' e ottenuto un "Accesso negato". –

+0

Scusa, non sembra funzionare. Si prega di verificare se si sta eseguendo come amministratore, che non dovrebbe essere il caso normale. – Jonno

+2

Mi dispiace provato, non funziona –

10

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

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

altrimenti ho ricevuto "Il parametro non è corretto"

+0

"Il parametro non è corretto" può anche restituito se non si specifica un trailing '/' nell'URL – LostSalad

5

È possibile avviare l'applicazione come amministratore se si aggiunge manifesto dell'applicazione per il tuo progetto.

Basta aggiungere un nuovo elemento al progetto e selezionare "File manifest di applicazione". Modificare l'elemento <requestedExecutionLevel> a:

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

Nel caso in cui si desidera utilizzare il flag "user = Everyone" è necessario regolare al vostro lingua del sistema. In inglese è come detto:

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

In tedesco si sarebbe:

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

Ho anche affrontato problem.If simile è già stato riservato url allora dovete eliminare prima l'url per l'esecuzione in in modalità non amministratore altrimenti fallirà con l'errore Accesso negato.

netsh http delete urlacl url=http://+:80 
3

Per impostazione predefinita di Windows definisce i seguenti prefisso che è a disposizione di tutti: http://+:80/Temporary_Listen_Addresses/

in modo da poter registrare il proprio HttpListener tramite:

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

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

3

In alternativa che non richiede l'elevazione o netsh si potrebbe anche usare TcpListener per esempio.

Il seguente è un estratto modificato di questo campione: 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 

di lettura e scrittura metodi essere:

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

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

+1

Vale la pena notando che il codice sopra ha dei bug in questo però. Innanzitutto usando StreamWriter con UTF8 causa problemi in alcuni browser, sto indovinando a causa di una BOM in uscita o qualcosa del genere. Ed è tutto in ASCII e va tutto bene. Ho anche aggiunto del codice per attendere che i dati diventino disponibili per la lettura sullo streaming poiché a volte non restituirebbe alcun dato. – mackie

+0

Grazie a @mackie: quella parte è stata presa direttamente dal campione stesso di Microsoft. Suppongo che non l'abbiano davvero testato correttamente. Se puoi modificare la mia risposta, procedi e correggi i metodi, altrimenti puoi inviarmi le modifiche e posso aggiornarlo. –