Accès HttpListener refusé

180

J'écris un serveur HTTP en C #.

Quand j'essaye d'exécuter la fonction, HttpListener.Start()je reçois un HttpListenerExceptiondicton

"Accès refusé".

Lorsque j'exécute l'application en mode administrateur dans Windows 7, cela fonctionne bien.

Puis-je le faire fonctionner sans le mode administrateur? si oui comment? Sinon, comment puis-je faire passer l'application en mode administrateur après le démarrage?

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
la source
Si quelqu'un veut éviter cette erreur, il peut essayer de l'écrire avec TcpListener. Il ne nécessite pas de privilèges d'administrateur
Vlad
Je suis confronté au même problème, dans Visual Studio 2008 + Windows 7, il produit une erreur `` Accès refusé '', pour contre-résoudre ce problème, il faut exécuter Visual Studio 2008 en mode administrateur
Mark Khor

Réponses:

307

Oui, vous pouvez exécuter HttpListener en mode non-administrateur. Tout ce que vous avez à faire est d'accorder des autorisations à l'URL particulière. par exemple

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

La documentation est ici .

Darrel Miller
la source
48
Ceci est utile, mais par souci d'exhaustivité, l'URL spécifiée dans cette ligne de code: httpListener.Prefixes.Add("http://*:4444/");doit correspondre EXACTEMENT à celle de la netshcommande. Par exemple, j'ai eu httpListener.Prefixes.Add("http://127.0.0.1:80/");la même netshcommande que vous, et l'exception HttpListenerException sera toujours lancée. J'avais besoin de changer httpListener.Prefixes.Add("http://+:80/");Merci pour votre aide @Darrel Miller, parce que vous m'avez mis sur la bonne voie pour comprendre cela!
psyklopz
10
Et n'oubliez pas la barre oblique de fin si "MyUri" est vide, sinon vous obtiendrez une The parameter is incorrecterreur. Exemple:url=http://+:80/
Igor Brejc
14
Existe-t-il un moyen de le faire pour un utilisateur non administratif, même pour http://localhost:80/? J'ai une application de bureau qui a besoin de recevoir une demande sur une telle URL, et il semble dommage d'exiger qu'un administrateur l'installe sur 50 ordinateurs de bureau, juste pour ce seul but.
John Saunders
5
Apparemment non. Il n'y a également aucune mention d'élévation ou de privilèges administratifs requis dans la page de documentation. Il est donc facile de supposer que cela agira comme un TCPListener "plus intelligent", alors qu'en réalité il y a une raison MAJEURE de ne pas l'utiliser - pourtant cela n'est pas documenté.
David Ford
4
L'exécution de netsh nécessite un administrateur; (
superfly71
27

Puis-je le faire fonctionner sans le mode administrateur? si oui comment? Sinon, comment puis-je faire passer l'application en mode administrateur après le démarrage?

Vous ne pouvez pas, cela doit commencer par des privilèges élevés. Vous pouvez le redémarrer avec le runasverbe, qui invitera l'utilisateur à passer en mode administrateur

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

EDIT: en fait, ce n'est pas vrai; HttpListener peut s'exécuter sans privilèges élevés, mais vous devez donner l'autorisation pour l'URL sur laquelle vous souhaitez écouter. Voir la réponse de Darrel Miller pour plus de détails.

Thomas Levesque
la source
Pouvez-vous expliquer pourquoi je dois commencer avec des privilèges élevés?
Randall Flagg
Vous devez également ajouter cette ligne 'startInfo.UseShellExecute = false;' avant 'Process.Start (startInfo);'
Randall Flagg
@Randall: parce que c'est ainsi que Windows fonctionne ... un processus ne peut pas passer en mode administrateur pendant son exécution. Concernant UseShellExecute: cela dépend de ce que vous exécutez. J'ai testé mon code avec "notepad.exe", cela fonctionne bien sans UseShellExecute = false
Thomas Levesque
Merci. À propos de UseShellExecute: J'ai essayé d'exécuter le code que j'ai publié. Un autre problème est que pour une raison quelconque, il m'a demandé une fois si je voulais me présenter en tant qu'administrateur et à tout autre moment après cela, il ne le demandait pas. J'ai redémarré, débogué pour m'assurer qu'il y va et rien. Aucune suggestion?
Randall Flagg
1
@Thomas Il n'y a aucun problème pour exécuter HttpListener en mode non-administrateur.
Darrel Miller
23

Si vous utilisez http://localhost:80/comme préfixe, vous pouvez écouter les requêtes http sans avoir besoin de privilèges administratifs.

Ehsan Mirsaeedi
la source
2
Pouvez-vous publier un exemple de code? J'ai juste essayé ceci avec http://localhost:80/et j'ai obtenu un "Accès refusé".
John Saunders
2
Désolé, cela ne semble pas fonctionner. Veuillez vérifier si vous exécutez en tant qu'administrateur, ce qui ne devrait pas être le cas normal.
Jonno
si cela fonctionnait, je le considérerais comme un bogue de Windows. peu importe ce que vous pensez de Windows, je suppose que c'est d'un niveau très basique que très, très probablement, ils n'ont pas manqué de gérer correctement.
hoijui
26
Donc, plus de 2 ans plus tard, cela fonctionne pour moi maintenant sur Windows Server 2008 R2 avec .NET Framework 4.5. httpListener.Prefixes.Add("http://*:4444/");montre en effet une Access Deniederreur mais httpListener.Prefixes.Add("http://localhost:4444/");fonctionne sans aucun problème. Il semble que Microsoft soit exclu localhostde ces restrictions. Fait intéressant, httpListener.Prefixes.Add("http://127.0.0.1:4444/");montre toujours une erreur d'accès refusé, donc la seule chose qui fonctionne estlocalhost:{any port}
Tony
1
@Tony merci, votre commentaire était en fait le plus précieux pour moi. J'avais "127.0.0.1" dans plusieurs configs. Le contrôle qualité a signalé qu'il ne fonctionnait pas sous Windows 8.1, mais qu'il était très bien dans Windows 10 (et je l'ai testé moi-même dans Win10). Curieusement, il semble que Microsoft a accordé à tout le monde l'utilisation de 127.0.0.1 dans Win10 mais pas dans la version 8.1 (et probablement dans les versions antérieures), mais bien sûr, localhost va bien. Merci
Adam Plocher
20

La syntaxe était incorrecte pour moi, vous devez inclure les guillemets:

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

sinon j'ai reçu "Le paramètre est incorrect"

Dave
la source
4
"Le paramètre est incorrect" peut également être renvoyé si vous ne spécifiez pas de fin /sur l'url
LostSalad
13

Si vous souhaitez utiliser l'indicateur "utilisateur = Tout le monde", vous devez l'adapter à la langue de votre système. En anglais, c'est comme mentionné:

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

En allemand, ce serait:

netsh http add urlacl url=http://+:80/ user=Jeder
Christoph Brückmann
la source
1
Dans les systèmes de langue espagnole, faites: user = Todos
Rodrigo Rodrigues
De plus, j'avais besoin de mettre l'URL entre guillemets, comme mentionné dans une autre réponse.
Carsten Führmann
10

Comme alternative qui ne nécessite pas d'élévation ou de netsh, vous pouvez également utiliser TcpListener par exemple.

Ce qui suit est un extrait modifié de cet exemple: 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

Les méthodes de lecture et d'écriture étant:

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
la source
Cela a été vraiment utile car nous avons eu le problème HttpListener lors de l'implémentation d'un IBrowser pour la bibliothèque IdentityModel.OidcClient, donc un cas d'utilisation très similaire à celui ci-dessus.
mackie
1
Il convient de noter que le code ci-dessus contient des bogues. Tout d'abord, l'utilisation d'un StreamWriter avec UTF8 pose des problèmes dans certains navigateurs, j'imagine à cause d'une nomenclature en cours de sortie ou quelque chose du genre. Je l'ai changé en ASCII et tout va bien. J'ai également ajouté du code pour attendre que les données soient disponibles pour lecture sur le flux, car elles ne renvoyaient parfois aucune donnée.
mackie
Merci @mackie - cette partie a été prise directement à partir du propre échantillon de Microsoft en fait. Je suppose qu'ils ne l'ont pas vraiment testé correctement. Si vous pouvez modifier ma réponse, continuez et corrigez les méthodes - sinon, envoyez-moi les modifications et je pourrai la mettre à jour.
Michael Olsen
2
Au lieu d'utiliser ASCII, il faut utiliser le constructeur suivant new UTF8Encoding(false), qui désactive l'émission d'une nomenclature. J'ai déjà changé cela dans la réponse
Sebastian
Je préfère cette solution à la réponse acceptée. Lancer netsh semble être un hack. C'est une solution programmatique solide. Merci!
Jim Gomes
7

Vous pouvez démarrer votre application en tant qu'administrateur si vous ajoutez le manifeste d'application à votre projet.

Ajoutez simplement un nouvel élément à votre projet et sélectionnez "Fichier manifeste de l'application". Remplacez l' <requestedExecutionLevel>élément par:

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

Par défaut, Windows définit le préfixe suivant qui est accessible à tous: http: // +: 80 / Temporary_Listen_Addresses /

Vous pouvez donc enregistrer votre HttpListenervia:

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

Cela pose parfois des problèmes avec des logiciels tels que Skype qui essaieront d'utiliser le port 80 par défaut.

Sébastien
la source
1
Skype était le coupable. J'ai changé le port pour qu'il ne soit pas 80 et cela a fonctionné.
Rendez-vous
5
httpListener.Prefixes.Add("http://*:4444/");

vous utilisez "*" donc vous exécutez la commande suivante en tant qu'administrateur

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

pas d'utilisation +, doit utiliser *, car vous spécifiez *: 4444 ~.

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

NamedStar
la source
C'était tout pour moi. Cela a fonctionné pour moi.Add("http://+:9000/")
John Gietzen
2

J'ai également rencontré un problème similaire.Si vous avez déjà réservé l'URL, vous devez d'abord supprimer l'URL pour l'exécuter en mode non administrateur, sinon elle échouera avec l'erreur Access is Denied.

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