2010-10-25 32 views
118

Estoy escribiendo un servidor HTTP en C#.HttpListener Acceso Denegado

Cuando trato de ejecutar la función HttpListener.Start() me sale un HttpListenerException diciendo

"Acceso denegado".

Cuando ejecuto la aplicación en modo de administrador en Windows 7, funciona bien.

¿Puedo hacer que funcione sin el modo de administración? Si es así, ¿cómo? Si no, ¿cómo puedo hacer que la aplicación cambie al modo de administración después de comenzar a ejecutar?

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

Si alguien quiere evitar que el error que se trata de escribir con TcpListener . No requiere privilegios de administrador – Vlad

Respuesta

232

Sí, puede ejecutar HttpListener en modo no administrador. Todo lo que necesita hacer es otorgar permisos a la URL en particular. p.ej.

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

La documentación es here.

+33

Esto es útil, pero para completar, la URL especificada en esta línea de código: 'httpListener.Prefixes.Add (" http: // *: 4444/");' debe coincidir EXACTAMENTE con la del comando 'netsh' . Por ejemplo, tenía 'httpListener.Prefixes.Add (" http://127.0.0.1:80/ ");' y el mismo comando 'netsh' que tiene, y aún se lanzará HttpListenerException. Necesitaba cambiar 'httpListener.Prefixes.Add (" http: // +: 80/");' Gracias por su ayuda @Darrel Miller, porque me pusieron en el camino correcto para descubrirlo. – psyklopz

+6

Y no olvides la barra inclinada si "MyUri" está vacío, de lo contrario obtendrás el error 'El parámetro es incorrecto'. Ejemplo: 'url = http: // +: 80 /' –

+11

¿Hay alguna manera de hacer esto para un usuario no administrativo, incluso para 'http: // localhost: 80 /'? Tengo una aplicación de escritorio que necesita recibir una solicitud en dicha URL, y parece una pena exigir que un administrador la instale en 50 computadoras de escritorio, solo para este fin. –

25

¿Puedo hacer que se ejecute sin el modo de administrador? Si es así, ¿cómo? Si no, ¿cómo puedo hacer que la aplicación cambie al modo de administración después de comenzar a correr?

No puede, tiene que comenzar con privilegios elevados. Puede reiniciarlo con el runas verbo, que le pedirá al usuario cambiar al modo de administrador

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

EDIT: En realidad, eso no es cierto; HttpListener puede ejecutarse sin privilegios elevados, pero debe otorgar permiso para la URL en la que desea escuchar. Ver Darrel Miller's answer para más detalles.

+0

¿Puede explicar por qué tengo que comenzar con privilegios elevados? –

+0

También necesita agregar esta línea 'startInfo.UseShellExecute = false;' antes de 'Process.Start (startInfo);' –

+0

@Randall: porque así es como funciona Windows ... un proceso no puede cambiar al modo de administración mientras se está ejecutando. En cuanto a UseShellExecute: depende de lo que está ejecutando. Probé mi código con "notepad.exe", funciona bien sin UseShellExecute = false –

14

Si usa http://localhost:80/ como prefijo, puede escuchar las solicitudes http sin necesidad de privilegios administrativos.

+0

¿Puedes publicar un código de ejemplo? Acabo de probar esto con 'http: // localhost: 80 /' y obtuve un "acceso denegado". –

+0

Lo siento, eso no parece funcionar. Compruebe si se está ejecutando como administrador, lo que no debería ser el caso normal. – Jonno

+2

Lo siento intentado esto, no funciona –

10

La sintaxis estaba mal para mí, debe incluir las comillas:

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

de lo contrario me recibieron "el parámetro es incorrecto"

+0

"El parámetro es incorrecto" puede también se devolverá si no especifica un '/' final en la url – LostSalad

5

Puede iniciar la aplicación como administrador si se agrega Manifiesto de aplicación a tu proyecto.

Solo agregue un nuevo elemento a su proyecto y seleccione "Archivo de manifiesto de aplicación". Cambiar el elemento <requestedExecutionLevel> a:

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

En caso de que quiera utilizar la bandera "user = Todos" tiene que ajustar a su idioma del sistema. En Inglés que es como se menciona:

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

en alemán sería:

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

también hice frente de problema.Si similares que ya han reservado url entonces usted tiene que eliminar primero la url para funcionar en En modo no administrador, fallará con el error Acceso denegado.

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

Por defecto, Windows define el siguiente prefijo que está disponible para todos: http://+:80/Temporary_Listen_Addresses/

para que pueda registrar su HttpListener a través de:

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

Esto a veces causa problemas con el software, tales como Skype, que tratará de utilizar el puerto 80 por defecto.

3

Como una alternativa que no requiere elevación o netsh también se podría utilizar TcpListener por ejemplo.

El siguiente es un extracto modificado de esta muestra: 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 

de lectura y escritura métodos ser:

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

Esto fue muy útil ya que teníamos el problema HttpListener cuando implementamos un IBrowser para la biblioteca IdentityModel.OidcClient por lo que un caso de uso muy similar al anterior. – mackie

+1

Vale la pena sin embargo, observando que el código anterior tiene errores. Primero, usar un StreamWriter con UTF8 causa problemas en algunos navegadores, supongo que debido a que se está produciendo una BOM o algo así. ed a ASCII y todo está bien. También agregué un código para esperar a que los datos estén disponibles para leer en la transmisión ya que a veces no devuelve datos. – mackie

+0

Gracias @mackie: esa parte fue tomada directamente de la propia muestra de Microsoft. Supongo que realmente no lo probaron correctamente. Si puede editar mi respuesta, proceda y solucione los métodos; de lo contrario, tal vez me envíe los cambios y pueda actualizarlos. –