HttpListener Отказано в доступе


180

Я пишу HTTP-сервер в C #.

Когда я пытаюсь выполнить функцию, HttpListener.Start()я получаю HttpListenerExceptionвысказывание

"В доступе отказано".

Когда я запускаю приложение в режиме администратора в Windows 7, оно работает нормально.

Можно ли заставить его работать без режима администратора? если да как? Если нет, как я могу перевести приложение в режим администратора после запуска?

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

Если кто-то хочет избежать этой ошибки, он может попробовать написать ее с помощью TcpListener. Не требует прав администратора
Влад

Я сталкиваюсь с той же проблемой, в Visual Studio 2008 + Windows 7, она выдает ошибку «Доступ запрещен», чтобы решить эту проблему, нужно запустить Visual Studio 2008 в режиме администратора
Марк Хор,

Ответы:


307

Да, вы можете запустить HttpListener в режиме без прав администратора. Все, что вам нужно сделать, это предоставить разрешения для конкретного URL. например

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

Документация здесь .


48
Это полезно, но для полноты URL-адрес, указанный в этой строке кода: httpListener.Prefixes.Add("http://*:4444/");должен точно соответствовать URL-адресу в netshкоманде. Например, у меня была httpListener.Prefixes.Add("http://127.0.0.1:80/");и та же netshкоманда, что и у вас, и HttpListenerException все равно будет выброшен. Мне нужно было изменить. httpListener.Prefixes.Add("http://+:80/");Спасибо за вашу помощь @Darrel Miller, потому что вы помогли мне разобраться в этом!
psyklopz

10
И не забывайте завершающий слеш, если «MyUri» пусто, иначе вы получите The parameter is incorrectошибку. Пример:url=http://+:80/
Игорь Брейц

14
Есть ли способ сделать это для пользователя без прав администратора, даже для http://localhost:80/? У меня есть настольное приложение, которому нужно получить один запрос на такой URL-адрес, и, кажется, стыдно требовать, чтобы администратор установил его на 50 рабочих столах, только для этой цели.
Джон Сондерс

5
Очевидно нет. Там также нет упоминания о повышении прав или административных привилегий, необходимых на странице документации. Таким образом, легко предположить, что это будет действовать как «умнее» TCPListener, когда в действительности есть ОСНОВНАЯ причина не использовать его - но это не задокументировано.
Дэвид Форд

4
Для запуска netsh требуется администратор; (
superfly71

27

Можно ли заставить его работать без режима администратора? если да как? Если нет, как я могу перевести приложение в режим администратора после запуска?

Вы не можете, это должно начаться с повышенных привилегий. Вы можете перезапустить его с помощью runasглагола, который предложит пользователю перейти в режим администратора

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

РЕДАКТИРОВАТЬ: на самом деле это не так; HttpListener может работать без повышенных привилегий, но вам нужно дать разрешение для URL, который вы хотите прослушать. Смотрите ответ Даррела Миллера для деталей.


Не могли бы вы объяснить, почему я должен начать с повышенных привилегий?
Рэндалл Флэгг

Вам также необходимо добавить эту строку 'startInfo.UseShellExecute = false;' перед 'Process.Start (startInfo);'
Рэндалл Флэгг

@Randall: потому что так работает Windows ... процесс не может переключиться в режим администратора во время работы. Относительно UseShellExecute: это зависит от того, что вы выполняете. Я проверил свой код с "notepad.exe", он отлично работает без UseShellExecute = false
Томас Левеск

Спасибо. О UseShellExecute: я попытался запустить опубликованный код. Другая проблема заключается в том, что по какой-то причине он однажды спросил меня, хочу ли я работать от имени администратора, а в любое другое время после этого не спрашивает. Я перезапустил, отладил, чтобы убедиться, что он идет туда и ничего. какие-либо предложения?
Рэндалл Флэгг

1
@Thomas Нет проблем с запуском HttpListener в режиме без прав администратора.
Даррел Миллер

23

Если вы используете http://localhost:80/префикс, вы можете прослушивать http-запросы без необходимости иметь права администратора.


2
Можете ли вы опубликовать пример кода? Я только что попробовал это http://localhost:80/и получил «Отказано в доступе».
Джон Сондерс

2
Извините, это не похоже на работу. Пожалуйста, проверьте, работаете ли вы как администратор, что не должно быть нормальным случаем.
Джонно

если это будет работать, я бы посчитал это ошибкой Windows. независимо от того, что вы думаете об окнах, я предполагаю, что это очень базовый уровень, который, скорее всего, они не упустили для правильной обработки.
Hoijui

26
Таким образом, более чем через 2 года это работает для меня на Windows Server 2008 R2 с .NET Framework 4.5. httpListener.Prefixes.Add("http://*:4444/");действительно показывает Access Deniedошибку, но httpListener.Prefixes.Add("http://localhost:4444/");работает без проблем. Похоже, Microsoft исключена localhostиз этих ограничений. Интересно, что по- httpListener.Prefixes.Add("http://127.0.0.1:4444/");прежнему отображается ошибка «Отказано в доступе», поэтому единственное, что работает,localhost:{any port}
Тони

1
@ Тони, спасибо, твой комментарий был для меня самым ценным. У меня был "127.0.0.1" в нескольких конфигах. QA сообщил, что он не работал на Windows 8.1, но был в Windows 10 просто отлично (и я сам протестировал его в Win10). Как ни странно, похоже, что Microsoft предоставила всем возможность использовать 127.0.0.1 в Win10, но не 8.1 (и, вероятно, более ранние версии), но, конечно же, с localhost все в порядке. Спасибо
Адам Плохер

20

Синтаксис был неверным для меня, вы должны включить кавычки:

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

в противном случае я получил "Параметр неверен"


4
«Неправильный параметр» также может быть возвращен , если вы не укажете завершающую /на URL
LostSalad

13

Если вы хотите использовать флаг «user = Everyone», вам нужно настроить его на свой язык системы. На английском это как упомянуто:

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

На немецком это будет:

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

1
В системах на испанском языке выполните: user = Todos
Rodrigo Rodrigues

Кроме того, мне нужно было поместить URL в кавычки, как упоминалось в другом ответе.
Карстен Фюрманн

10

В качестве альтернативы, которая не требует повышения прав или netsh, вы также можете использовать TcpListener, например.

Ниже приведен измененный отрывок из этого примера: 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

Методы чтения и записи:

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

Это было действительно полезно, поскольку у нас возникла проблема с HttpListener при реализации IBrowser для библиотеки IdentityModel.OidcClient, поэтому пример использования очень похож на приведенный выше.
Макки

1
Стоит отметить, что в приведенном выше коде есть ошибки. Во-первых, использование StreamWriter с UTF8 вызывает проблемы в некоторых браузерах, я полагаю, из-за выходной спецификации или чего-то в этом роде. Я изменил его на ASCII и все хорошо. Я также добавил некоторый код, чтобы дождаться, когда данные станут доступны для чтения в потоке, поскольку иногда он не возвращает никаких данных.
Мэки

Спасибо @mackie - эта часть была взята непосредственно из собственного образца Microsoft. Я предполагаю, что они действительно не проверяли это должным образом. Если вы можете отредактировать мой ответ, исправьте методы - в противном случае, возможно, пришлите мне изменения, и я смогу обновить их.
Майкл Олсен

2
Вместо использования ASCII следует использовать следующий конструктор new UTF8Encoding(false), который отключает выдачу спецификации. Я уже изменил это в ответе
Себастьян

Я предпочитаю это решение над принятым ответом. Запуск netsh кажется хаком. Это солидное программное решение. Спасибо!
Джим Гомес

7

Вы можете запустить свое приложение как администратор, если добавите Application Manifest в свой проект.

Просто добавьте новый элемент в ваш проект и выберите «Файл манифеста приложения». Измените <requestedExecutionLevel>элемент на:

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

7

По умолчанию Windows определяет следующий префикс, доступный каждому: http: // +: 80 / Temporary_Listen_Addresses /

Таким образом, вы можете зарегистрировать свой HttpListenerчерез:

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

Это иногда вызывает проблемы с программным обеспечением, таким как Skype, которое будет пытаться использовать порт 80 по умолчанию.


1
Skype был виновником. Я изменил порт, чтобы не быть 80, и это сработало.
Перейти к

5
httpListener.Prefixes.Add("http://*:4444/");

вы используете "*", чтобы выполнить следующий cmd как администратор

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

не использовать +, должен использовать *, потому что вы специфицируете *: 4444 ~.

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


Это было для меня. Это сработало для меня.Add("http://+:9000/")
Джон Гитцен

2

Я также столкнулся с подобной проблемой. Если вы уже зарезервировали URL-адрес, то сначала необходимо удалить URL-адрес, чтобы запустить его в режиме без прав администратора, иначе произойдет сбой с ошибкой Access is Denied.

netsh http delete urlacl url=http://+:80
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.