Odmowa dostępu HttpListener

180

Piszę serwer HTTP w C #.

Kiedy próbuję wykonać tę funkcję HttpListener.Start(), słyszę HttpListenerExceptionpowiedzenie

"Brak dostępu".

Kiedy uruchamiam aplikację w trybie administratora w systemie Windows 7, działa dobrze.

Czy mogę to uruchomić bez trybu administratora? jeśli tak, jak? Jeśli nie, jak mogę zmienić aplikację w tryb administratora po uruchomieniu?

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
źródło
Jeśli ktoś chce uniknąć tego błędu, może spróbować napisać go za pomocą TcpListener. Nie wymaga uprawnień administratora
Vlad
Napotykam ten sam problem, w Visual Studio 2008 + Windows 7 generuje błąd
Mark Khor

Odpowiedzi:

307

Tak, możesz uruchomić HttpListener w trybie innym niż administrator. Wszystko, co musisz zrobić, to przyznać uprawnienia do określonego adresu URL. na przykład

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

Dokumentacja jest tutaj .

Darrel Miller
źródło
48
Jest to pomocne, ale dla kompletności adres URL określony w tym wierszu kodu: httpListener.Prefixes.Add("http://*:4444/");musi być DOKŁADNIE zgodny z adresem w netshpoleceniu. Na przykład miałem httpListener.Prefixes.Add("http://127.0.0.1:80/");i to samo netshpolecenie, które masz, a HttpListenerException nadal będzie generowany. Musiałem to zmienić. httpListener.Prefixes.Add("http://+:80/");Dziękuję za pomoc @Darrel Miller, ponieważ wprowadziłeś mnie na właściwą ścieżkę, aby to rozgryźć!
psyklopz
10
I nie zapomnij o końcowym ukośniku, jeśli „MyUri” jest puste, w przeciwnym razie pojawi się The parameter is incorrectbłąd. Przykład:url=http://+:80/
Igor Brejc
14
Czy istnieje sposób, aby to zrobić dla użytkownika bez uprawnień administracyjnych, nawet dla http://localhost:80/? Mam aplikację komputerową, która musi otrzymać jedno żądanie na taki adres URL i szkoda wymagać, aby administrator zainstalował ją na 50 komputerach stacjonarnych, tylko w tym jednym celu.
John Saunders,
5
Najwyraźniej nie. Na stronie dokumentacji nie ma również wzmianki o konieczności podniesienia uprawnień lub uprawnień administracyjnych. Łatwo więc założyć, że będzie to „mądrzejszy” odtwarzacz TCPListener, podczas gdy w rzeczywistości istnieje GŁÓWNY powód, aby go nie używać - jednak nie jest to udokumentowane.
David Ford
4
Uruchomienie netsh wymaga administratora; (
superfly71
27

Czy mogę to uruchomić bez trybu administratora? jeśli tak, jak? Jeśli nie, jak mogę zmienić aplikację w tryb administratora po uruchomieniu?

Nie możesz, musi zacząć się od podwyższonych uprawnień. Możesz go ponownie uruchomić za pomocą runasczasownika, który poprosi użytkownika o przejście do trybu administratora

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

EDYCJA: właściwie to nieprawda; HttpListener może działać bez podwyższonych uprawnień, ale musisz nadać uprawnienia do adresu URL, na którym chcesz nasłuchiwać. Zobacz odpowiedź Darrela Millera po szczegóły.

Thomas Levesque
źródło
Czy możesz wyjaśnić, dlaczego muszę zacząć od wyższych uprawnień?
Randall Flagg
Musisz również dodać tę linię „startInfo.UseShellExecute = false;” przed 'Process.Start (startInfo);'
Randall Flagg
@Randall: ponieważ tak działa system Windows ... proces nie może przełączyć się w tryb administratora, gdy jest uruchomiony. Odnośnie UseShellExecute: zależy to od tego, co wykonujesz. Przetestowałem mój kod za pomocą „notepad.exe”, działa dobrze bez UseShellExecute = false
Thomas Levesque
Dzięki. O UseShellExecute: próbowałem uruchomić kod, który opublikowałem. Innym problemem jest to, że z jakiegoś powodu zapytał mnie raz, czy chcę działać jako administrator, a innym razem nie pyta. Zrestartowałem, debugowałem, aby upewnić się, że tam idzie i nic. jakieś sugestie?
Randall Flagg
1
@Thomas Nie ma problemu z uruchomieniem HttpListener w trybie nieadministracyjnym.
Darrel Miller
23

Jeśli używasz http://localhost:80/jako prefiksu, możesz słuchać żądań http bez konieczności posiadania uprawnień administracyjnych.

Ehsan Mirsaeedi
źródło
2
Czy możesz opublikować przykładowy kod? Właśnie próbowałem tego http://localhost:80/i otrzymałem komunikat „Odmowa dostępu”.
John Saunders,
2
Przepraszam, to nie działa. Sprawdź, czy pracujesz jako administrator, co nie powinno być normalnym przypadkiem.
Jonno,
gdyby to zadziałało, uznałbym to za błąd systemu Windows. bez względu na to, co myślisz o oknach, zakładam, że jest to na bardzo podstawowym poziomie, którego bardzo, bardzo prawdopodobne, że nie przeoczyli poprawnie.
hoijui
26
Tak więc ponad 2 lata później działa to teraz w systemie Windows Server 2008 R2 z platformą .NET Framework 4.5. httpListener.Prefixes.Add("http://*:4444/");rzeczywiście pokazuje Access Deniedbłąd, ale httpListener.Prefixes.Add("http://localhost:4444/");działa bez problemu. Wygląda na to, że Microsoft został wyłączony localhostz tych ograniczeń. Co ciekawe, httpListener.Prefixes.Add("http://127.0.0.1:4444/");nadal pokazuje błąd odmowy dostępu, więc jedyne, co działa, tolocalhost:{any port}
Tony
1
@ Tony dzięki, Twój komentarz był dla mnie najbardziej wartościowy. Miałem „127.0.0.1” w kilku konfiguracjach. Kontrola jakości zgłosiła, że ​​nie działa w systemie Windows 8.1, ale działał dobrze w systemie Windows 10 (i sam to przetestowałem w systemie Win10). Co dziwne, wygląda na to, że Microsoft pozwolił wszystkim używać 127.0.0.1 w Win10, ale nie 8.1 (i prawdopodobnie wcześniejszych wersji), ale z pewnością lokalny host jest w porządku. Dzięki
Adam Plocher
20

Składnia była dla mnie zła, musisz dołączyć cudzysłowy:

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

w przeciwnym razie otrzymałem komunikat „Parametr jest nieprawidłowy”

Dave
źródło
4
Komunikat „Parametr jest nieprawidłowy” można również zwrócić, jeśli nie określisz końcowego /
adresu
13

Jeśli chcesz użyć flagi „user = Wszyscy”, musisz dostosować ją do języka systemu. W języku angielskim jest jak wspomniano:

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

W języku niemieckim byłoby to:

netsh http add urlacl url=http://+:80/ user=Jeder
Christoph Brückmann
źródło
1
W systemach hiszpańskojęzycznych wykonaj: user = Todos
Rodrigo Rodrigues
Dodatkowo musiałem umieścić adres URL w cudzysłowie, jak wspomniano w innej odpowiedzi.
Carsten Führmann
10

Jako alternatywę, która nie wymaga podniesienia uprawnień ani netsh, możesz również użyć na przykład TcpListener.

Poniżej znajduje się zmodyfikowany fragment tego przykładu: 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

Metody odczytu i zapisu to:

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
źródło
Było to naprawdę przydatne, ponieważ mieliśmy problem z HttpListener podczas implementowania IBrowser dla biblioteki IdentityModel.OidcClient, więc bardzo podobny przypadek użycia do powyższego.
mackie
1
Warto zauważyć, że powyższy kod zawiera błędy. Po pierwsze, użycie StreamWriter z UTF8 powoduje problemy w niektórych przeglądarkach, zgaduję, że jest to wynik BOM lub coś w tym rodzaju. Zmieniłem to na ASCII i wszystko jest w porządku. Dodałem również kod, aby czekać, aż dane staną się dostępne do odczytu w strumieniu, ponieważ czasami nie zwracał żadnych danych.
mackie
Dzięki @mackie - ta część została faktycznie pobrana bezpośrednio z własnej próbki Microsoftu. Myślę, że tak naprawdę nie przetestowali tego właściwie. Jeśli możesz edytować moją odpowiedź, napraw metody - w przeciwnym razie może wyślij mi zmiany, a ja mogę je zaktualizować.
Michael Olsen,
2
Zamiast używać ASCII należy użyć następującego konstruktora new UTF8Encoding(false), który wyłącza emisję BOM. Zmieniłem to już w odpowiedzi
Sebastian
Wolę to rozwiązanie od zaakceptowanej odpowiedzi. Uruchamianie netsh wygląda jak hack. To solidne rozwiązanie programistyczne. Dzięki!
Jim Gomes
7

Możesz uruchomić aplikację jako administrator, jeśli dodasz manifest aplikacji do swojego projektu.

Po prostu dodaj nowy element do swojego projektu i wybierz „Plik manifestu aplikacji”. Zmień <requestedExecutionLevel>element na:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
R.Titov
źródło
7

Domyślnie system Windows definiuje następujący prefiks, który jest dostępny dla wszystkich: http: // +: 80 / Temporary_Listen_Addresses /

Możesz więc zarejestrować się HttpListenerprzez:

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

Czasami powoduje to problemy z oprogramowaniem takim jak Skype, które domyślnie będzie próbowało używać portu 80.

Sebastian
źródło
1
Skype był winowajcą. Zmieniłem port na nie 80 i zadziałało.
GoTo
5
httpListener.Prefixes.Add("http://*:4444/");

używasz "*", więc wykonujesz następujące polecenie cmd jako administrator

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

nie używaj +, musisz używać *, ponieważ podałeś *: 4444 ~.

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

NamedStar
źródło
To było to dla mnie. Udało mi się.Add("http://+:9000/")
John Gietzen
2

Miałem też podobny problem, jeśli już zarezerwowałeś adres URL, musisz najpierw usunąć adres URL, aby uruchomić w trybie innym niż administrator, w przeciwnym razie nie powiedzie się z błędem Odmowa dostępu.

netsh http delete urlacl url=http://+:80
vivek nuna
źródło