Akses HttpListener Ditolak

180

Saya menulis server HTTP dalam C #.

Ketika saya mencoba menjalankan fungsi HttpListener.Start()saya mendapat HttpListenerExceptionpepatah

"Akses ditolak".

Ketika saya menjalankan aplikasi dalam mode admin di windows 7 berfungsi dengan baik.

Bisakah saya membuatnya berjalan tanpa mode admin? jika ya bagaimana? Jika tidak, bagaimana saya bisa membuat aplikasi berubah ke mode admin setelah mulai berjalan?

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
sumber
Jika seseorang ingin menghindari kesalahan itu, ia dapat mencoba menulisnya dengan TcpListener. Itu tidak memerlukan hak admin
Vlad
Saya menghadapi masalah yang sama, dalam Visual Studio 2008 + Windows 7, itu menghasilkan kesalahan 'Akses ditolak', untuk mengatasi ini dengan menjalankan Visual Studio 2008 dalam Mode Admin
Mark Khor

Jawaban:

307

Ya, Anda dapat menjalankan HttpListener dalam mode non-admin. Yang perlu Anda lakukan adalah memberikan izin ke URL tertentu. misalnya

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

Dokumentasi ada di sini .

Darrel Miller
sumber
48
Ini membantu, tetapi untuk kelengkapan, URL yang ditentukan dalam baris kode ini: httpListener.Prefixes.Add("http://*:4444/");harus sama persis dengan yang ada di netshperintah. Sebagai contoh, saya punya httpListener.Prefixes.Add("http://127.0.0.1:80/");dan netshperintah yang sama yang Anda miliki, dan HttpListenerException masih akan dibuang. Saya perlu mengubah httpListener.Prefixes.Add("http://+:80/");Terima kasih atas bantuan Anda @ Charles Miller, karena Anda membuat saya di jalan yang benar untuk mencari tahu!
psyklopz
10
Dan jangan lupa trailing slash jika "MyUri" kosong, jika tidak Anda akan mendapatkan The parameter is incorrectkesalahan. Contoh:url=http://+:80/
Igor Brejc
14
Apakah ada cara untuk melakukan ini untuk pengguna non-administratif, bahkan untuk http://localhost:80/? Saya memiliki aplikasi desktop yang perlu menerima satu permintaan pada URL seperti itu, dan tampaknya memalukan untuk meminta administrator menginstalnya di 50 desktop, hanya untuk satu tujuan ini.
John Saunders
5
Sepertinya tidak. Juga tidak disebutkan tentang ketinggian atau hak administratif yang diperlukan di halaman dokumentasi. Jadi mudah untuk berasumsi bahwa ini akan bertindak sebagai TCPListener "pintar", padahal sebenarnya ada alasan utama untuk tidak menggunakannya - namun ini tidak didokumentasikan.
David Ford
4
Menjalankan netsh memerlukan admin; (
superfly71
27

Bisakah saya membuatnya berjalan tanpa mode admin? jika ya bagaimana? Jika tidak, bagaimana saya bisa membuat aplikasi berubah ke mode admin setelah mulai berjalan?

Anda tidak bisa, itu harus dimulai dengan hak istimewa yang ditinggikan. Anda dapat memulai kembali dengan runaskata kerja, yang akan meminta pengguna untuk beralih ke mode admin

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

EDIT: sebenarnya, itu tidak benar; HttpListener dapat berjalan tanpa hak yang lebih tinggi, tetapi Anda perlu memberikan izin untuk URL yang ingin Anda dengarkan. Lihat jawaban Darrel Miller untuk detailnya.

Thomas Levesque
sumber
Bisakah Anda jelaskan mengapa saya harus mulai dengan hak istimewa yang ditinggikan?
Randall Flagg
Anda juga perlu menambahkan baris ini 'startInfo.UseShellExecute = false;' sebelum 'Process.Start (startInfo);'
Randall Flagg
@Rallall: karena itulah cara Windows bekerja ... proses tidak dapat beralih ke mode admin saat sedang berjalan. Mengenai UseShellExecute: itu tergantung pada apa yang Anda jalankan. Saya menguji kode saya dengan "notepad.exe", berfungsi dengan baik tanpa UseShellExecute = false
Thomas Levesque
Terima kasih. Tentang UseShellExecute: Saya mencoba menjalankan kode yang saya posting. Masalah lain adalah karena alasan tertentu ia bertanya kepada saya sekali jika saya ingin menjalankan sebagai administrator dan lain kali setelah itu ia tidak bertanya. Saya memulai kembali, Debugged untuk memastikan tidak ada. ada saran?
Randall Flagg
1
@ Thomas Tidak ada masalah menjalankan HttpListener dalam mode non-admin.
Darrel Miller 6-10
23

Jika Anda menggunakan http://localhost:80/sebagai awalan, Anda dapat mendengarkan permintaan http tanpa perlu hak administratif.

Ehsan Mirsaeedi
sumber
2
Bisakah Anda memposting beberapa kode contoh? Saya baru saja mencoba ini http://localhost:80/dan mendapat "Akses Ditolak".
John Saunders
2
Maaf, itu sepertinya tidak berhasil. Harap periksa apakah Anda menjalankan sebagai administrator, yang seharusnya tidak menjadi kasus normal.
Jonno
jika ini akan berhasil, saya akan menganggapnya sebagai bug windows. tidak peduli apa yang Anda pikirkan tentang windows, saya menganggap ini sebagai tingkat yang sangat dasar yang sangat, sangat mungkin mereka tidak ketinggalan untuk menangani dengan benar.
hoijui
26
Jadi lebih dari 2 tahun kemudian, ini berfungsi untuk saya sekarang di Windows Server 2008 R2 dengan .NET framework 4.5. httpListener.Prefixes.Add("http://*:4444/");memang menunjukkan Access Deniedkesalahan tetapi httpListener.Prefixes.Add("http://localhost:4444/");berfungsi tanpa masalah. Sepertinya Microsoft dikecualikan localhostdari pembatasan ini. Menariknya, httpListener.Prefixes.Add("http://127.0.0.1:4444/");masih menunjukkan kesalahan Access Denied, jadi satu-satunya yang berfungsi adalahlocalhost:{any port}
Tony
1
@Tony terima kasih, komentar Anda sebenarnya yang paling berharga bagi saya. Saya punya "127.0.0.1" di beberapa konfigurasi. QA melaporkan bahwa itu tidak berfungsi pada Windows 8.1 tetapi di Windows 10 baik-baik saja (dan saya mengujinya sendiri di Win10). Anehnya, tampaknya Microsoft telah memberikan izin kepada setiap orang untuk menggunakan 127.0.0.1 di Win10 tetapi tidak 8.1 (dan mungkin versi sebelumnya), tetapi cukup yakin, localhost baik-baik saja. Terima kasih
Adam Plocher
20

Sintaksnya salah untuk saya, Anda harus menyertakan tanda kutip:

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

kalau tidak saya menerima "Parameter salah"

Dave
sumber
4
"Parameter salah" juga dapat dikembalikan jika Anda tidak menentukan trailing /di url
LostSalad
13

Jika Anda ingin menggunakan flag "user = Everyone" Anda harus menyesuaikannya dengan bahasa sistem Anda. Dalam bahasa Inggris disebutkan:

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

Di jerman itu adalah:

netsh http add urlacl url=http://+:80/ user=Jeder
Christoph Brückmann
sumber
1
Dalam sistem berbahasa Spanyol, lakukan: user = Todos
Rodrigo Rodrigues
Plus, saya perlu memasukkan URL dalam tanda kutip, sebagaimana disebutkan dalam jawaban lain.
Carsten Führmann
10

Sebagai alternatif yang tidak memerlukan elevasi atau netsh Anda juga bisa menggunakan TcpListener misalnya.

Berikut ini adalah kutipan modifikasi dari sampel ini: 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

Metode baca dan tulis adalah:

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
sumber
Ini benar-benar berguna karena kami memiliki masalah HttpListener ketika mengimplementasikan IBrowser untuk perpustakaan IdentityModel.OidcClient sehingga kasus penggunaan yang sangat mirip dengan yang di atas.
mackie
1
Perlu dicatat bahwa kode di atas memiliki bug di dalamnya. Pertama menggunakan StreamWriter dengan UTF8 menyebabkan masalah di beberapa browser, saya menduga karena BOM menjadi output atau sesuatu seperti itu. Saya mengubahnya menjadi ASCII dan semuanya baik-baik saja. Saya juga menambahkan beberapa kode untuk menunggu data tersedia untuk dibaca di sungai karena terkadang tidak mengembalikan data.
mackie
Terima kasih @ Mackie - bagian itu diambil langsung dari sampel Microsoft sendiri sebenarnya. Saya kira mereka tidak benar-benar mengujinya dengan benar. Jika Anda dapat mengedit jawaban saya, lanjutkan dan perbaiki metode - jika tidak, kirimkan saya perubahan dan saya dapat memperbaruinya.
Michael Olsen
2
Alih-alih menggunakan ASCII orang harus menggunakan konstruktor berikut new UTF8Encoding(false), yang menonaktifkan memancarkan BOM. Saya sudah mengubah ini dalam jawaban
Sebastian
Saya lebih suka solusi ini daripada jawaban yang diterima. Menjalankan netsh tampaknya seperti retasan. Ini adalah solusi terprogram yang solid. Terima kasih!
Jim Gomes
7

Anda dapat memulai aplikasi Anda sebagai administrator jika Anda menambahkan Application Manifest ke proyek Anda.

Cukup Tambahkan Item Baru ke proyek Anda dan pilih "File Aplikasi Manifest". Ubah <requestedExecutionLevel>elemen menjadi:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
R.Titov
sumber
7

Secara default Windows mendefinisikan awalan berikut yang tersedia untuk semua orang: http: // +: 80 / Temporary_Listen_Addresses /

Jadi Anda dapat mendaftarkan Anda HttpListenermelalui:

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

Ini terkadang menyebabkan masalah dengan perangkat lunak seperti Skype yang akan mencoba memanfaatkan port 80 secara default.

Sebastian
sumber
1
Skype adalah biang keladinya. Saya mengubah port menjadi tidak 80 dan berhasil.
GoTo
5
httpListener.Prefixes.Add("http://*:4444/");

Anda menggunakan "*" sehingga Anda menjalankan cmd berikut sebagai admin

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

tidak ada gunanya +, harus menggunakan *, karena Anda spec *: 4444 ~.

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

NamedStar
sumber
Ini untuk saya. Ini berhasil bagi saya untuk.Add("http://+:9000/")
John Gietzen
2

Saya juga menghadapi masalah yang sama. Jika Anda sudah memesan url maka Anda harus terlebih dahulu menghapus url untuk berjalan dalam mode non admin, jika tidak maka akan gagal dengan kesalahan Akses ditolak.

netsh http delete urlacl url=http://+:80
vivek nuna
sumber