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" />
R.Titov
источник
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

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

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

netsh http delete urlacl url=http://+:80
Вивек Нуна
источник