Переписать ХОСТ с использованием ASP.NET C # - PullRequest
0 голосов
/ 28 декабря 2018

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

В данный момент я просто работаю в Visual Studio.Адрес будет:

http://externalsite.company.com/testapi/myapi.asmx/GetNewKey

Я хочу изменить его на:

http://internalsite1.local/testapi/myapi.asmx/GetNewKey

в зависимости от значения в заголовке "hostingAuth".

Там будетзаголовки и тело, поступающие на страницу, которые будут использоваться сервером "internalsiteX.local".Это будет варьироваться от компании к компании, поэтому я не могу учесть все возможности.

На данный момент мой токен безопасности является заголовком «hostingAuth», и в приведенном ниже примере единственными действительными токенами являются «company1secret» и"company2secret"

Я думаю, что мне нужен модуль перезаписи, но для этого требуется статический код перезаписи / перенаправления в web.config (Intelligencia.UrlRewriter.RewriterHttpModule).Там будет сотни записей, поэтому я не хочу статический файл, я хочу использовать базу данных, чтобы она могла быть изменена кодом.Я не могу использовать (возможно?) Надстройку IIS ARR, так как мне нужно, чтобы компании были защищены маркером безопасности.

Я ищу что-то подобное, кроме "urlRequestContext.Request.Url.Хост "- это ПОЛУЧЕНИЕ только не SET

Global.asax.cs:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext urlRequestContext = HttpContext.Current;

    if (!(urlRequestContext.Request.Url.AbsolutePath.ToLower().StartsWith("/errorpages/")))
    {
        try
        {
            string hostingAuth = urlRequestContext.Request.Headers.GetValues("hostingAuth").FirstOrDefault();
            if (hostingAuth == "company1secret")
            {
                urlRequestContext.Request.Url.Host = "internalsite1.local";
            }
            if (hostingAuth == "company2secret")
            {
                urlRequestContext.Request.Url.Host = "internalsite2.local";
            }
        }
        catch (Exception ex)
        {
            Response.Redirect("/errorpages/missingtoken.aspx", true);
        }
    }
}

Я не могу найти пример того, как это сделать.Это либо очень просто и не стоит каких-либо примеров, либо невозможно.У кого-нибудь есть предложения?Я совершенно неправильно подхожу к этому?

Спасибо

1 Ответ

0 голосов
/ 02 января 2019

Оказывается, это не простое решение, и вы не меняете хост.

ARR почти сработал с некоторой изобретательностью, написав заголовок хоста в качестве имени сервера и домена, за исключением URL-адреса.перезапись работала в первый раз, но игнорировалась во второй раз, когда приложение отправляло информацию обратно на внутренний сервер.

Подошло только одно решение обратного прокси, которое было: https://gist.github.com/anth-3/6169292

, чтооснова для кода.Основная идея:

Для полноты код включен.Это несколько черновиков, но не доработано или проверено, так что ждите ошибок.Кажется, что он работает со всеми глаголами * 1, но в основном тестируется с GET, и это также работает с https * 2

* 1, если вы хотите, чтобы PUT, DELETE и другие глаголы работали, вам нужно включить это вIIS, и, похоже, вам нужно сделать это и в файле web.config

* 2, чтобы https работал, веб-сайту внешнего веб-сайта требуется действующий сертификат для его работы.Это было проверено с использованием сертификата подстановочного знака на внешнем и внутреннем веб-сайте в течение https://443

Это позволило мне выполнить первоначальный POC.

Единственные страницы в пустом проекте:

global.asax

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext urlRequestContext = HttpContext.Current;

    if (!(urlRequestContext.Request.Url.AbsolutePath.ToLower().StartsWith("/errorpages/")))
    {
        try
        {
            string hostingAuth = urlRequestContext.Request.Headers.GetValues("hostingauth").FirstOrDefault();

            if (hostingAuth == "company2secret")
            {
                string newHost = "company2internal.validdomain.com";
                reverseProxy.ProcessRequest(urlRequestContext, newHost);
            }
        }
        catch (System.Threading.ThreadAbortException)
        {
            // ignore it
        }

        catch (Exception ex)
        {
            Response.Redirect("/errorpages/missingtoken.aspx", true);
        }
    }
}

.

class1.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;

namespace redirect
{
    public class reverseProxy
    {
        //public static bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
        //{
        //    return true;
        //}

        public static void ProcessRequest(HttpContext Context, string newHost)
        {

            //ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);
            //ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            /* Create variables to hold the request and response. */
            HttpRequest Request = Context.Request;
            HttpResponse Response = Context.Response;

            string URI = null;
            URI = Request.Url.Scheme.ToString() + "://" + newHost + Request.Url.PathAndQuery;

            /* Create an HttpWebRequest to send the URI on and process results. */
            System.Net.HttpWebRequest ProxyRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(URI);


            /* Set the same requests to our request as in the incoming request */
            ProxyRequest.Method = Request.HttpMethod.ToUpper();
            ProxyRequest.ServicePoint.Expect100Continue = false;
            ProxyRequest.Accept = Request.Headers["Accept"];
            //ProxyRequest.TransferEncoding = Request.Headers["Accept-encoding"];
            ProxyRequest.SendChunked = false;
            //ProxyRequest.Date = Request.Headers["Date"];
            ProxyRequest.Expect = Request.Headers["Expect"];
            //ProxyRequest.IfModifiedSince = Request.Headers["If-Modified-Since"];
            //ProxyRequest.Range = Request.Headers["Range"];
            ProxyRequest.Referer = Request.Headers["Referer"];
            ProxyRequest.TransferEncoding = Request.Headers["Transfer-Encoding"];
            ProxyRequest.UserAgent = Request.Headers["User-Agent"];

            //set the same headers except for certain ones as they need to be set not in this way
            foreach (string strKey in Request.Headers.AllKeys)
            {
                if ((strKey != "Accept") && (strKey != "Connection") && (strKey != "Content-Length") && (strKey != "Content-Type") && (strKey != "Date") && (strKey != "Expect") && (strKey != "Host") && (strKey != "If-Modified-Since") && (strKey != "Range") && (strKey != "Referer") && (strKey != "Transfer-Encoding") && (strKey != "User-Agent") && (strKey != "Proxy-Connection") && (strKey != "hostingauth"))
                ProxyRequest.Headers.Add(strKey, Request.Headers[strKey]);
            }

            if (Request.InputStream.Length > 0)
            {
                /* 
                 * Since we are using the same request method as the original request, and that is 
                 * a POST, the values to send on in the new request must be grabbed from the 
                 * original POSTed request.
                 */
                byte[] Bytes = new byte[Request.InputStream.Length];
                Request.InputStream.Read(Bytes, 0, (int)Request.InputStream.Length);
                ProxyRequest.ContentLength = Bytes.Length;
                string ContentType = Request.ContentType;

                if (String.IsNullOrEmpty(ContentType))
                {
                    ProxyRequest.ContentType = "application/x-www-form-urlencoded";
                }
                else
                {
                    ProxyRequest.ContentType = ContentType;
                }

                using (Stream OutputStream = ProxyRequest.GetRequestStream())
                {
                    OutputStream.Write(Bytes, 0, Bytes.Length);
                }
            }
            //else
            //{
            //    /*
            //     * When the original request is a GET, things are much easier, as we need only to 
            //     * pass the URI we collected earlier which will still have any parameters 
            //     * associated with the request attached to it.
            //     */
            //    //ProxyRequest.Method = "GET";
            //}

            System.Net.WebResponse ServerResponse = null;

            /* Send the proxy request to the remote server or fail. */
            try
            {
                //even if it isn't gzipped it tries but ignores if it fails
                ProxyRequest.AutomaticDecompression = DecompressionMethods.GZip;
                ServerResponse = ProxyRequest.GetResponse();
            }
            catch (System.Net.WebException WebEx)
            {
                #region exceptionError
                Response.StatusCode = 500;
                Response.StatusDescription = WebEx.Status.ToString();
                Response.Write(WebEx.Message);
                Response.Write("\r\n");
                Response.Write(((System.Net.HttpWebResponse)WebEx.Response).ResponseUri);
                Response.Write("\r\n");
                Response.Write(((System.Net.HttpWebResponse)WebEx.Response).Method);
                Response.Write("\r\n");
                Response.Write("Headers\r\n");
                foreach (string strKey in Request.Headers.AllKeys)
                {
                    Response.Write(strKey + ": " +Request.Headers[strKey]);
                    Response.Write("\r\n");
                }
                Response.End();
                #endregion
                return;
            }

            /* Set up the response to the client if there is one to set up. */
            if (ServerResponse != null)
            {
                Response.ContentType = ServerResponse.ContentType;
                using (Stream ByteStream = ServerResponse.GetResponseStream())
                {
                    /* What is the response type? */
                    if (ServerResponse.ContentType.Contains("text") ||
                            ServerResponse.ContentType.Contains("json") ||
                            ServerResponse.ContentType.Contains("xml"))
                    {
                        /* These "text" types are easy to handle. */
                        using (StreamReader Reader = new StreamReader(ByteStream))
                        {
                            string ResponseString = Reader.ReadToEnd();

                            /* 
                             * Tell the client not to cache the response since it 
                             * could easily be dynamic, and we do not want to mess
                             * that up!
                             */
                            Response.CacheControl = "no-cache";

                            //If the request came with a gzip request, send it back gzipped
                            if (Request.Headers["Accept-encoding"].Contains("gzip"))
                            {
                                Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
                                      System.IO.Compression.CompressionMode.Compress);
                                Response.AppendHeader("Content-Encoding", "gzip");
                            }

                            //write webpage/results back
                            Response.Write(ResponseString);

                        }
                    }
                    else
                    {

                        //This is completely untested

                        /* 
                         * Handle binary responses (image, layer file, other binary 
                         * files) differently than text.
                         */
                        BinaryReader BinReader = new BinaryReader(ByteStream);

                        byte[] BinaryOutputs = BinReader.ReadBytes((int)ServerResponse.ContentLength);

                        BinReader.Close();

                        /* 
                         * Tell the client not to cache the response since it could 
                         * easily be dynamic, and we do not want to mess that up!
                         */
                        Response.CacheControl = "no-cache";

                        //could this make it more efficient - untested
                        if (Request.Headers["Accept-encoding"].Contains("gzip"))
                        {
                            Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
                                  System.IO.Compression.CompressionMode.Compress);
                            Response.AppendHeader("Content-Encoding", "gzip");
                        }


                        /*
                         * Send the binary response to the client.
                         * (Note: if large images/files are sent, we could modify this to 
                         * send back in chunks instead...something to think about for 
                         * future.)
                         */
                        Response.OutputStream.Write(BinaryOutputs, 0, BinaryOutputs.Length);
                    }
                    ServerResponse.Close();
                }
            }

            //done
            Response.End();
        }


    }
}

.

В файле web.config показано переопределение глагола для PUT и DELETE для работы.Я знаю, что я бы обратный прокси aspx и API без расширения.Это, вероятно, излишне и неэффективно, но работает

web.config

<!--
  For more information on how to configure your ASP.NET application, please visit
  https://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.6.1"/>
    <httpRuntime targetFramework="4.6.1"/>
  </system.web>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs"
        type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701"/>
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
        type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+"/>
    </compilers>
  </system.codedom>
  <system.webServer>
    <handlers>
      <remove name="PageHandlerFactory-ISAPI-2.0-64" />
      <remove name="PageHandlerFactory-ISAPI-2.0" />
      <remove name="PageHandlerFactory-Integrated-4.0" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="PageHandlerFactory-ISAPI-4.0_32bit" />
      <remove name="PageHandlerFactory-ISAPI-4.0_64bit" />
      <remove name="PageHandlerFactory-Integrated" />
      <add name="PageHandlerFactory-Integrated" path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv2.0" />
      <add name="PageHandlerFactory-ISAPI-4.0_64bit" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="PageHandlerFactory-ISAPI-4.0_32bit" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="PageHandlerFactory-Integrated-4.0" path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" />
      <add name="PageHandlerFactory-ISAPI-2.0" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
      <add name="PageHandlerFactory-ISAPI-2.0-64" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
    </handlers>
  </system.webServer>

</configuration>

/ errorpages / missingtoken.aspx

<body>
the token is missing
</body>

Если вам интересно узнать speed , Почтальон давал мне время

  • 350-600ms прямо через Интернет для отображения страницы .aspx.
  • 600-900мс через обратный прокси-сервер (2 разных местоположения. RP дома, внутренний сервер (представлен снаружи) на сайте).Я подозреваю, что это происходит из-за многократного распаковывания и повторного распаковывания.
  • 800 мс-1 с, если он проходил через RP, но GZIPping не выполнялся.

Если мне не нужно было разархивировать, чтобы разархивировать, я подозреваю, что время будет ближе кпрямое время

Если бы я мог развернуть RP на месте и запросить его у внутреннего сервера в разархивированном виде и представить обратно в заархивированном виде, это могло бы быть и быстрее.YMMV

.

Если вам интересно, зачем это нужно, - это вставка аутентификации в запрос к API, который обычно находится в белом списке IP.RP не будет внесен в белый список, и у меня нет доступа к коду API, чтобы изменить его.

...