在IIS上对WCF服务进行身份验证时仅在第一次调用AuthenticateRequest时

问题描述:

我有一个OData WCF服务的工作实现,现在需要使用基本自定义身份验证在IIS中发布。在IIS上对WCF服务进行身份验证时仅在第一次调用AuthenticateRequest时

实现基于Microsoft OData example,在IIS Express下工作得很好。当我只将基本身份验证发布到IIS 7.5时,AuthenticateRequest处理程序仅在初始请求时被调用,它返回状态码401并要求进行身份验证。

AuthenticateRequest不再被后续请求调用。在IIS上调试服务时,肯定会调用BeginRequest,它只是AuthenticateRequest不在管道中存在?在IIS Express中都会调用这两个请求。

IIS身份验证配置:

IIS Authentication configuration

IHttpModule的代码:

public class BasicAuthModule: IHttpModule 
{ 
    // based on http://msdn.microsoft.com/en-gb/data/gg192997.aspx 

    public void Init(HttpApplication app) 
    { 
     app.AuthenticateRequest += AuthenticateRequest; 
     app.BeginRequest += BeginRequest; 
    } 

    private void BeginRequest(object sender, EventArgs e) 
    { 
     var app = (HttpApplication)sender; 
     if(app.Context == null) 
     { 
      throw new Exception("Will not happen"); 
     } 
    } 

    private void AuthenticateRequest(object sender, EventArgs e) 
    { 
     var app = (HttpApplication)sender; 
     if(!app.Request.Headers.AllKeys.Contains("Authorization")) 
     { 
      CreateNotAuthorizedResponse(app, 401, 1, "Please provide Authorization headers with your request."); 
      app.CompleteRequest(); 
     } 
     else if(!BasicAuthProvider.Authenticate(app.Context)) 
     { 
      CreateNotAuthorizedResponse(app, 401, 1, "Logon failed."); 
      app.CompleteRequest(); 
     } 
    } 

    private static void CreateNotAuthorizedResponse(HttpApplication app, int code, int subCode, string description) 
    { 
     var response = app.Context.Response; 
//  response.Status = "401 Unauthorized"; 
     response.StatusCode = code; 
     response.SubStatusCode = subCode; 
     response.StatusDescription = description; 
//  response.AppendHeader("WWW-Authenticate", "Basic"); 
//  response.End(); 
    } 

    public void Dispose() 
    { 
    } 
} 

Web.config文件:

<?xml version="1.0"?> 
<configuration> 
    <appSettings> 
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /> 
    </appSettings> 
    <system.web> 
    <compilation debug="true" targetFramework="4.5" /> 
    <httpRuntime targetFramework="4.5"/> 
    </system.web> 
    <system.serviceModel> 
    <behaviors> 
     <serviceBehaviors> 
     <behavior> 
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/> 
      <serviceDebug includeExceptionDetailInFaults="false"/> 
     </behavior> 
     </serviceBehaviors> 
    </behaviors> 
    <protocolMapping> 
     <add binding="basicHttpsBinding" scheme="https" /> 
    </protocolMapping>  
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> 
    </system.serviceModel> 
    <system.webServer> 
     <modules runAllManagedModulesForAllRequests="true"> 
      <add name="BasicAuthModule" type="WcfTestService.BasicAuthModule"/> 
     </modules> 
    <directoryBrowse enabled="true"/> 
    </system.webServer> 
</configuration> 

而且这样的问题:为什么认证工作在Visual Studio 2012的调试服务器但不在IIS 7.5中? 完整的测试项目可以从here下载。

编辑:

我在CreateNotAuthorizedResponse功能到Response.End和(),这导致了异常(我加了它在发布前最后一分钟)注释掉过度的测试代码。

当检查请求时,它看起来像一切都应该工作,除了Cookie可能导致IIS由于某种原因而导致IIS跳过身份验证。下面第一2原始请求 - 回复对:

请求1:

GET http://localhost:8080/test/ HTTP/1.1 
Host: localhost:8080 
Connection: keep-alive 
Pragma: no-cache 
Cache-Control: no-cache 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36 
Accept-Encoding: gzip,deflate,sdch 
Accept-Language: en-US,en;q=0.8,pl;q=0.6 
Cookie: ASPSESSIONIDAQRDDBTR=BPCFKGDDCGJLPFKHEPOLPMFK; __RequestVerificationToken_L2RlbGl2ZXJ50=j0o-RDC12Z_E1o1nnXU_9iFaThUEPXRXDNKepqoX2fmgjg8gRB6Hi9fs3MSGxUvYQs6tJ0Jxsf6U20WKWpOrj4azgL_VpVzQHcNyJghUrKg1; __RequestVerificationToken=uOeCVgZDguOs3mRA7O4nhj88wJ_mFR6t1QN7vl7mOPGaNBoEnVFmIQVoUwxim8NbODJKMz5fBuAoPKo7Ek-4JeujsOIyIxjRB1xS_JaFF381; .ASPXAUTH=C2965A60E4BB162123A2CDDA8FD825C9DF3625116E5722C9B873BA64F041CCDCAB098EA3A208C2061D8D5746BC0832413105BA274C1B37DB8276471D49DE12562E4E93933289828427F559057519E75421493909E215EAA0DFB4C8DBE213EAC19AB6025EA715658A8D57CAFA308F7AC4A9051687777D2E82B7A2552917466E7C0BFA0C23EEE272F7E83C3718371375358B1199F155FB882EF8F5082CB28F6E030146DE365B5E4D8FE25E55EDD3F03788 

回复1:(由CreateNotAuthorizedResponse方法创建)

HTTP/1.1 401 Please provide Authorization headers with your request. 
Cache-Control: private 
Content-Type: text/html; charset=utf-8 
Server: Microsoft-IIS/7.5 
WWW-Authenticate: Basic realm="localhost" 
X-Powered-By: ASP.NET 
Date: Mon, 20 Oct 2014 13:03:14 GMT 
Content-Length: 6607 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>IIS 7.5 Detailed Error - 401.1 - Please provide Authorization headers with your request.</title> 

请求2(输入时测试:测试 - dGVzdDp0ZXN0) :

GET http://localhost:8080/test/ HTTP/1.1 
Host: localhost:8080 
Connection: keep-alive 
Pragma: no-cache 
Cache-Control: no-cache 
Authorization: Basic dGVzdDp0ZXN0 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36 
Accept-Encoding: gzip,deflate,sdch 
Accept-Language: en-US,en;q=0.8,pl;q=0.6 
Cookie: ASPSESSIONIDAQRDDBTR=BPCFKGDDCGJLPFKHEPOLPMFK; __RequestVerificationToken_L2RlbGl2ZXJ50=j0o-RDC12Z_E1o1nnXU_9iFaThUEPXRXDNKepqoX2fmgjg8gRB6Hi9fs3MSGxUvYQs6tJ0Jxsf6U20WKWpOrj4azgL_VpVzQHcNyJghUrKg1; __RequestVerificationToken=uOeCVgZDguOs3mRA7O4nhj88wJ_mFR6t1QN7vl7mOPGaNBoEnVFmIQVoUwxim8NbODJKMz5fBuAoPKo7Ek-4JeujsOIyIxjRB1xS_JaFF381; .ASPXAUTH=C2965A60E4BB162123A2CDDA8FD825C9DF3625116E5722C9B873BA64F041CCDCAB098EA3A208C2061D8D5746BC0832413105BA274C1B37DB8276471D49DE12562E4E93933289828427F559057519E75421493909E215EAA0DFB4C8DBE213EAC19AB6025EA715658A8D57CAFA308F7AC4A9051687777D2E82B7A2552917466E7C0BFA0C23EEE272F7E83C3718371375358B1199F155FB882EF8F5082CB28F6E030146DE365B5E4D8FE25E55EDD3F03788 

响应2(BeginRequest被调用但不是AuthenticateRequest):

HTTP/1.1 401 Unauthorized 
Cache-Control: private 
Content-Type: text/html; charset=utf-8 
Server: Microsoft-IIS/7.5 
WWW-Authenticate: Basic realm="localhost" 
X-Powered-By: ASP.NET 
Date: Mon, 20 Oct 2014 13:03:17 GMT 
Content-Length: 6531 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>IIS 7.5 Detailed Error - 401.1 - Unauthorized</title> 

我认为你正在将IIS内置的基本认证与你自己的定制认证模块混合在一起。简短的答案是在IIS中禁用基本身份验证并启用匿名。这将通过所有的身份验证工作到asp.net上。

如果你在VS测试我假设你是通过浏览器这样做,当你点击F5时自动启动。

随着基本身份验证打开IIS最初回应与401导致浏览器显示登录窗体。

您输入的凭据必须有有效的Windows凭据,IIS会根据您的Windows帐户进行验证。一旦IIS验证了这些凭证,它就会将请求传递给您的代码。

如果输入有效的Windows凭据的情况下被提出,但你的代码将拒绝它,因为凭据不是检验/试验并返回一个401.1

如果你进入测试/测试则IIS被拒绝证书,并发送回401,所以你的事件永远不会被调用。结束语:您应该使用http客户端(例如使用System.Net.WebClient进行单元测试)测试您的Web服务,或者使用Chrome插件(邮递员/ devhttp)在http级别进行测试。如果你已经这样做了,那就原谅我的假设。

+0

谢谢你的一个很好的解释。事实上,我已经转向匿名,现在随着你的描述,它变得更有意义。我使用Fiddler来分析来自IIS的流量,单元测试正如您所描述的那样直接连接到它,或者主要使用DataServiceContext和Credentials集。感谢你的宝贵时间。 – too 2014-10-23 15:20:37

+0

不客气,我很高兴这有帮助!你发布了一个详细的问题并提供了代码来帮助你了解它。干杯 – Avner 2014-10-23 15:40:53