Служба 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>