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

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 .


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.


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.


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”


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

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

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" />

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.


1
Skype był winowajcą. Zmieniłem port na nie 80 i zadziałało.
GoTo


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.