Доступ 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/");повинна точно відповідати вказаній у 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);
}

EDIT: насправді це неправда; HttpListener може працювати без підвищених привілеїв, але вам потрібно дати дозвіл на URL-адресу, за якою ви хочете слухати. Детальніше дивіться у відповідь Даррела Міллера .


Чи можете ви поясніть, чому я повинен починати з підвищених привілеїв?
Рандалл Флагг

Вам також потрібно додати цей рядок 'startInfo.UseShellExecute = false;' перед 'Process.Start (startInfo);'
Рандалл Флагг

@Randall: тому що так працює Windows ... процес не може перейти в режим адміністратора, поки він працює. Щодо UseShellExecute: це залежить від того, що ви виконуєте. Я перевірив свій код на "notepad.exe", він прекрасно працює без UseShellExecute = false
Thomas Levesque

Дякую. Про UseShellExecute: Я намагався запустити код, який я опублікував. Ще одна проблема полягає в тому, що чомусь він запитав мене один раз, чи хочу я працювати адміністратором, а в інший час після цього він не запитує. Я перезапустив, налагоджений, щоб переконатися, що він там йде і нічого. будь-які пропозиції?
Рандалл Флагг

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

23

Якщо ви використовуєте http://localhost:80/як префікс, ви можете прослуховувати http-запити, не потребуючи адміністративних привілеїв.


2
Чи можете ви надіслати якийсь приклад коду? Я просто спробував це http://localhost:80/і отримав "Заборонений доступ".
Джон Сондерс

2
Вибачте, це, здається, не працює. Перевірте, чи працюєте ви адміністратором, що не повинно бути нормальним.
Jonno

якщо це буде працювати, я вважав би це помилка 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 = Every", вам потрібно налаштувати його на вашу систему. Англійською мовою це як зазначено:

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

Німецькою мовою це буде:

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

1
В іспаномовних системах виконайте: user = Todos
Rodrigo Rodrigues

Плюс до цього, мені потрібно було поставити URL у лапки, про що говорилося в іншій відповіді.
Carsten Führmann

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, тому дуже подібний випадок використання до вищевказаного.
mackie

1
Варто відзначити, що код вище містить помилки в ньому. По-перше, використання StreamWriter з UTF8 спричиняє проблеми в деяких браузерах, я думаю, через вихід BOM або щось подібне. Я змінив його на ASCII і все добре. Я також додав код, щоб чекати, коли дані стануть доступними для читання в потоці, оскільки іноді вони не повертають ніяких даних.
mackie

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

2
Замість використання ASCII слід використовувати наступний конструктор new UTF8Encoding(false), який вимикає випромінювання BOM. Я змінив це вже у відповіді
Себастьян

Я вважаю за краще це рішення над прийнятою відповіддю. Запуск 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
Винувателем був скайп. Я змінив порт , щоб НЕ бути 80 , і вона працювала.
GoTo

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-адресу, щоб запустити його в режимі без адміністрування.

netsh http delete urlacl url=http://+:80
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.