IIS хостинг B2B WCF REST-аутентификация - PullRequest
0 голосов
/ 27 апреля 2020

Служба WCF предоставляет конечную точку REST, к которой обращается бизнес-процесс клиента. Я хочу иметь систему безопасности, которая проверяет имя пользователя и пароль из входящего запроса, чтобы убедиться, что это только от этого клиента.

Из исследования этой топи c кажется, что Basi c Аутентификация Точная подгонка, но ему нужен собственный HTTPModule и запись в web.config, чтобы он работал в «моде B2B».

Я ожидаю, что: 1) HttpModule будет обрабатывать аутентификацию, и я могу оставить конфигурация IIS как есть. т.е. мне не нужно включать Basi c Auth на уровне сайта с помощью диспетчера IIS.

2) Мой тестовый POST 'No Auth' POST против службы должен работать, когда Auth выключен, и завершиться ошибкой 401, когда Auth включен. (включается и выключается под управлением HttpModule и его связанных с web.config значений)

3) При включенной аутентификации HttpModule включение Basi c Auth в Postman для POST должно заставить его работать снова.

Так что, если Basi c Auth не лучший вариант, пожалуйста, сообщите.

Моя проблема заключается в том, что, как только я добавляю запись HTTPModule в элемент System.Web в сети. При обращении к config на сайте идет 500.

Далее следуют HTTP-модуль и Web.config. (Модуль HTTP собран из различных источников.)

Источник: Модуль HTTP

public class BasicAuthHttpModule:IHttpModule
{
    private static readonly log4net.ILog Log = log4net.LogManager.GetLogger
        (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    public void Init(HttpApplication app)
    {

        app.AuthenticateRequest += OnApplicationAuthenticateRequest;
        app.EndRequest += OnApplicationEndRequest;
        app.AcquireRequestState += new EventHandler(app_AcquireRequestState);
        app.PostAcquireRequestState += new EventHandler(app_PostAcquireRequestState);
    }

    private void app_AcquireRequestState(object o, EventArgs ea)
    {
        HttpApplication httpApp = (HttpApplication)o;
        HttpContext ctx = HttpContext.Current;
        ctx.Response.Write(" Executing AcquireRequestState ");
    }

    private void app_PostAcquireRequestState(object o, EventArgs ea)
    {
        HttpApplication httpApp = (HttpApplication)o;
        HttpContext ctx = HttpContext.Current;
        ctx.Response.Write(" Executing PostAcquireRequestState ");
    }

    private static void SetPrincipal(IPrincipal principal)
    {
        Thread.CurrentPrincipal = principal;
        if (HttpContext.Current != null)
        {
            HttpContext.Current.User = principal;
        }
    }
    /// <summary>
    /// Validate the user and password
    /// which are stored in Web.Config
    /// </summary>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <returns></returns>
    private static bool CheckPassword(string username, string password)
    {
        if(Log.IsDebugEnabled)
            Log.Debug($"Auth attempt User {username} password {password}");
        string sUser = ConfigurationManager.AppSettings["ReceiptUser"];
        string sPassword = ConfigurationManager.AppSettings["ReceiptPassword"];
        bool result = username == sUser && password == sPassword;
        if(Log.IsDebugEnabled)
            Log.Debug("Checkpassword result is " + result);
        return result;
    }
    private static void AuthenticateUser(string credentials)
    {
        try
        {
            if (Log.IsDebugEnabled)
            {
                Log.Debug($"Authentication Attempt credentials {credentials}");
            }

            var encoding = Encoding.GetEncoding("iso-8859-1");
            credentials = encoding.GetString(Convert.FromBase64String(credentials));

            int separator = credentials.IndexOf(':');
            string name = credentials.Substring(0, separator);
            string password = credentials.Substring(separator + 1);
            if(Log.IsDebugEnabled)
                Log.Debug($"About to Check Password username{name} password{password}");
            if (CheckPassword(name, password))
            {
                if (Log.IsDebugEnabled)
                    Log.Debug($"Password Checked OK username{name} password{password}");
                var identity = new GenericIdentity(name);
                SetPrincipal(new GenericPrincipal(identity, null));
            }
            else
            {
                if (Log.IsDebugEnabled)
                    Log.Debug($"Password check failed return 401 username{name} password{password}");
                // Invalid username or password.
                HttpContext.Current.Response.StatusCode = 401;
            }
        }
        catch (FormatException)
        {
            // Credentials were not formatted correctly.
            HttpContext.Current.Response.StatusCode = 401;
            Log.Error("Credentials not correctly formatted");
        }
    }
    /// <summary>
    /// Extract the Authorization Header.
    /// If Header exists parse it  and authenticate it
    /// If it doesn't exist then check if basic auth is turned on.
    /// If it is turned on throw a 401.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
    {
        if(Log.IsDebugEnabled)
            Log.Debug("Authentication Hit");
        var request = HttpContext.Current.Request;
        var authHeader = request.Headers["Authorization"];
        if (authHeader != null)
        {
            if (Log.IsDebugEnabled)
                Log.Debug($"OnApplicationAuthenticateRequest request.Headers[\"Authorization\"] is {authHeader} ");
            var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);

            // RFC 2617 sec 1.2, "scheme" name is case-insensitive
            if (authHeaderVal.Scheme.Equals("basic",
                    StringComparison.OrdinalIgnoreCase) &&
                authHeaderVal.Parameter != null)
            {
                if (Log.IsDebugEnabled)
                    Log.Debug("OnApplicationAuthenticateRequest about to authenticate basic user parameter  " + authHeaderVal.Parameter);
                AuthenticateUser(authHeaderVal.Parameter);
            }
        }
        else
        {
            bool basicAuth = bool.Parse(ConfigurationManager.AppSettings["BasicAuth"]);
            if (!basicAuth)
            {
                if (Log.IsDebugEnabled)
                    Log.Debug("OnApplicationAuthenticateRequest request.Headers[\"Authorization\"] is null and basicAuth is not turned on in Web.Config ");
                return;
            }

            if (Log.IsDebugEnabled)
                Log.Debug("OnApplicationAuthenticateRequest request.Headers[\"Authorization\"] is null ");
            HttpContext.Current.Response.StatusCode = 401;
        }
    }

    // If the request was unauthorized, add the WWW-Authenticate header 
    // to the response.
    private static void OnApplicationEndRequest(object sender, EventArgs e)
    {
        string Realm = "WCCReceipt";
        var response = HttpContext.Current.Response;
        if (response.StatusCode == 401)
        {
            response.Headers.Add("WWW-Authenticate",
                string.Format("Basic realm=\"{0}\"", Realm));
        }
    }

    public void Dispose()
    {

    }
}

Web.Config

    <?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net, Version=1.2.15.0, Culture=neutral" />
  </configSections>
  <connectionStrings>
    <add name="xxxxx" connectionString="Data Source=xxxxxx;Initial Catalog=xxxxxx;User ID=xxxxxx;Password=xxxxxxx" providerName="System.Data.SqlClient"/>
  </connectionStrings>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
    <add key="OriginID" value="19" />
    <add key="Diag" value="true" />
    <add key="Receiptuser" value="xxxx"/>
    <add key="ReceiptPassword" value ="xxxxx"/>
    <add key="BasicAuth" value="false"/>
    <add key="CheckPayableTicket" value="false"/>
  </appSettings>
  **<system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5"/>
    <httpModules>
      <add name="BasicAuthHttpModule" type="xxxxx.BasicAuthHttpModule"/>
      <!--<add name="BasicAuthHttpModule" type="WebHostBasicAuth.Modules.BasicAuthHttpModule, xxxxx"/>-->
    </httpModules>
  </system.web>**
  <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="webHttpBindingJSON" receiveTimeout="00:01:00">
          <security mode="Transport"/>
        </binding>
      </webHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="ServiceBehaviour" name="xxxxx.xxxxx">
        <endpoint address="" behaviorConfiguration="web" binding="webHttpBinding" contract="xxxxx.Ixxxxx" />
        <endpoint address="JSON" behaviorConfiguration="RESTJSONEndPointBehavior" binding="webHttpBinding" bindingConfiguration="webHttpBindingJSON" name="JSON" contract="xxxxxx.Ixxxxx" />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp />
        </behavior>
        <behavior name="RESTJSONEndPointBehavior">
          <webHttp  defaultOutgoingResponseFormat="Json" />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviour">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <log4net debug="false">
    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
      <threshold value="ALL" />
      <file value="Log/Log.txt" />
      <appendToFile value="true" />
      <rollingStyle value="Size" />
      <maxSizeRollBackups value="5" />
      <maximumFileSize value="5MB" />
      <staticLogFileName value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date %level %logger[%thread] - %message%newline" />
      </layout>
    </appender>
    <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">
      <threshold value="ALL" />
      <logName value="NetVendor" />
      <applicationName value="NetVendor" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%level %logger[%thread] - %message%newline" />
      </layout>
    </appender>
    <root>
      <priority value="ALL" />
      <appender-ref ref="RollingFileAppender" />
      <appender-ref ref="EventLogAppender" />
    </root>
    <category name="DesktopLogger.Form1">
      <priority value="ALL" />
    </category>
  </log4net>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        To browse web app root directory during debugging, set the value below to true.
        Set to false before deployment to avoid disclosing web app folder information.
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

1 Ответ

0 голосов
/ 28 апреля 2020

Когда веб-сайт размещен в IIS, Basic authentication предоставляется BasicAutenticationModule IIS. и он всегда аутентифицирует клиента с учетной записью windows сервера. поэтому я не думаю, что наш пользовательский модуль аутентификации basi c будет работать.
Вот еще одно решение для пользовательской аутентификации в веб-сервисе WCF Rest, желая, чтобы оно было полезным для вас.
Как реализовать настраиваемая аутентификация в службе WCF
Не стесняйтесь, дайте мне знать, если есть что-то, с чем я могу помочь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...