WCF服务提供了一个REST端点,客户业务流程可以访问该端点。 我希望有一个安全系统,该系统可以验证传入请求中的用户名和密码,以确保仅来自此客户。
通过探讨该主题,基本身份验证似乎很合适,但它需要自定义HTTPModule和web.config中的一个条目才能以“ B2B方式”工作。
我的期望是: 1)HttpModule将处理身份验证,我可以保留IIS配置。即,我不必使用IIS管理器在网站级别启用基本身份验证。
2)我对服务的测试邮递员“无身份验证” POST在Auth关闭时应该可以工作,而在Auth开启时失败401。 (由HttpModule及其web.config关联值控制打开和关闭)
3)在启用HttpModule Auth的情况下,在Postman中启用POST的Basic Auth应该可以使其重新工作。
因此,如果“基本身份验证”不是最好的选择,请提出建议。
我的问题是,一旦我将HTTPModule条目添加到web.config中的'System.Web'元素中,则该网站在访问时将变为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>
答案 0 :(得分:0)
当网站托管在IIS中时,Basic authentication
由IIS的BasicAutenticationModule
提供。并且始终使用服务器的Windows帐户对客户端进行身份验证。因此,我认为我们的自定义基本身份验证模块无法正常工作。
这是WCF Rest Web服务中自定义身份验证的另一种解决方案,希望它对您有用。
How to Implement custom authentication in WCF service
随时让我知道是否有什么可以帮助您的。