摘要 : 最近遇到了一個奇怪的 WCF 安全配置問題, WCF Service 上面配置了Windows Authentication. IIS上也啓用了 Windows Authentication, 可是仍然出現IIS沒有啓用Windows Authentication的問題. 在網絡上能查到的資料不多. 經過本身的troubleshooting發現所遇到的錯誤提示比較具備迷惑性. 因此POST上來給你們分享一下.web
最近遇到了一個奇怪的 WCF 安全配置問題, WCF Service 上面配置了Windows Authentication. IIS上也啓用了 Windows Authentication, 可是仍然出現IIS沒有啓用Windows Authentication的問題. 然而在啓動這個WCF Service的時候遇到了以下的錯誤. windows
Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NotSupportedException: Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[NotSupportedException: Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service.]
System.ServiceModel.Channels.HttpChannelListener.ApplyHostedContext(VirtualPathExtension virtualPathExtension, Boolean isMetadataListener) +15710645
System.ServiceModel.Channels.HttpsChannelListener.ApplyHostedContext(VirtualPathExtension virtualPathExtension, Boolean isMetadataListener) +27
System.ServiceModel.Channels.HttpsTransportBindingElement.BuildChannelListener(BindingContext context) +105
System.ServiceModel.Channels.BindingContext.BuildInnerChannelListener() +95
System.ServiceModel.Channels.MessageEncodingBindingElement.InternalBuildChannelListener(BindingContext context) +102
System.ServiceModel.Channels.TextMessageEncodingBindingElement.BuildChannelListener(BindingContext context) +70
System.ServiceModel.Channels.BindingContext.BuildInnerChannelListener() +95
System.ServiceModel.Channels.Binding.BuildChannelListener(Uri listenUriBaseAddress, String listenUriRelativeAddress, ListenUriMode listenUriMode, BindingParameterCollection parameters) +166
System.ServiceModel.Description.DispatcherBuilder.MaybeCreateListener(Boolean actuallyCreate, Type[] supportedChannels, Binding binding, BindingParameterCollection parameters, Uri listenUriBaseAddress, String listenUriRelativeAddress, ListenUriMode listenUriMode, ServiceThrottle throttle, IChannelListener& result, Boolean supportContextSession) +399
System.ServiceModel.Description.DispatcherBuilder.BuildChannelListener(StuffPerListenUriInfo stuff, ServiceHostBase serviceHost, Uri listenUri, ListenUriMode listenUriMode, Boolean supportContextSession, IChannelListener& result) +499
System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost) +1937
System.ServiceModel.ServiceHostBase.InitializeRuntime() +61
System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout) +63
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) +563
System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +135
System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +654
[ServiceActivationException: The service '/WinAuthService.svc' cannot be activated due to an exception during compilation. The exception message is: Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service..]
System.ServiceModel.AsyncResult.End(IAsyncResult result) +15786048
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +15706393
System.ServiceModel.Activation.HostedHttpRequestAsyncResult.ExecuteSynchronous(HttpApplication context, Boolean flowContext) +265
System.ServiceModel.Activation.HttpModule.ProcessRequest(Object sender, EventArgs e) +227
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +171
下面是這個WCF Service的配置. 按照這個配置, 是須要在IIS上啓用Windows Authentication.安全
<system.serviceModel> <bindings> <basicHttpBinding> <binding name="myBasicBinding"> <security mode="Transport"> <transport clientCredentialType="Windows" /> </security> </binding> </basicHttpBinding> </bindings> <services> <service name="WCFSample.WinAuthService"> <endpoint address="WinAuthService.svc" binding="basicHttpBinding" bindingConfiguration="myBasicBinding" contract="WCFSample.IWinAuthService" /> </service> </services> </system.serviceModel>
在IIS的管理界面上, 已經按照啓用了Windows Authentication而且禁用了Anonymous Authentication. 網絡
報錯信息的最頂上一條已經提示了拋出這個異常的CALL STACK 是 System.ServiceModel.Channels.HttpChannelListener.ApplyHostedContext. 由於是託管代碼, 因此用工具ILSPY來檢查這段代碼看什麼狀況下會拋出這樣的錯誤. 從代碼上看, 這個異常是在作了必定邏輯的校驗以後, 主動拋出來.app
internal override void ApplyHostedContext(VirtualPathExtension virtualPathExtension, bool isMetadataListener) { ServiceNameCollection customServiceNames; base.ApplyHostedContext(virtualPathExtension, isMetadataListener); AuthenticationSchemes authenticationSchemes = HostedTransportConfigurationManager.MetabaseSettings.GetAuthenticationSchemes(base.HostedVirtualPath); if (this.AuthenticationScheme == AuthenticationSchemes.Anonymous && (authenticationSchemes & AuthenticationSchemes.Anonymous) == AuthenticationSchemes.None && isMetadataListener) { if ((authenticationSchemes & AuthenticationSchemes.Negotiate) == AuthenticationSchemes.None) { this.authenticationScheme = authenticationSchemes; } else { this.authenticationScheme = AuthenticationSchemes.Negotiate; } } if ((authenticationSchemes & this.AuthenticationScheme) == AuthenticationSchemes.None) { if (!AuthenticationSchemesHelper.IsWindowsAuth(this.AuthenticationScheme)) { ExceptionUtility exceptionUtility = DiagnosticUtility.ExceptionUtility; object[] str = new object[] { this.AuthenticationScheme.ToString() }; throw exceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString("Hosting_AuthSchemesRequireOtherAuth", str))); } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString("Hosting_AuthSchemesRequireWindowsAuth"))); } if (this.AuthenticationScheme != AuthenticationSchemes.Anonymous) { ExtendedProtectionPolicy extendedProtectionPolicy = HostedTransportConfigurationManager.MetabaseSettings.GetExtendedProtectionPolicy(base.HostedVirtualPath); if (extendedProtectionPolicy == null) { if (this.extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Always) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString("ExtendedProtectionNotSupported"))); } } else if (!isMetadataListener || !ChannelBindingUtility.IsDefaultPolicy(this.extendedProtectionPolicy)) { ChannelBindingUtility.ValidatePolicies(extendedProtectionPolicy, this.extendedProtectionPolicy, true); if (this.usingDefaultSpnList) { customServiceNames = null; } else { customServiceNames = this.extendedProtectionPolicy.CustomServiceNames; } if (!ChannelBindingUtility.IsSubset(extendedProtectionPolicy.CustomServiceNames, customServiceNames)) { object[] objArray = new object[] { SR.GetString("Hosting_ExtendedProtectionSPNListNotSubset") }; string str1 = SR.GetString("Hosting_ExtendedProtectionPoliciesMustMatch2", objArray); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(str1)); } } else { this.extendedProtectionPolicy = extendedProtectionPolicy; } } if (!ServiceHostingEnvironment.IsSimpleApplicationHost) { this.realm = HostedTransportConfigurationManager.MetabaseSettings.GetRealm(virtualPathExtension.VirtualPath); } }
不過因爲不是咱們確切看到的錯誤. 因此須要在DLL的Resources中確切的驗證一下. 在這裏明確ide
Hosting_AuthSchemesRequireWindowsAuth=Security settings for this service require Windows Authentication but it is not enabled for the IIS application that hosts this service.工具
往上判斷, 可以進入到 if ((authenticationSchemes & this.AuthenticationScheme) == AuthenticationSchemes.None) . AuthenticationSchemes是一個枚舉類型. 在這裏注意到了有兩個分開的定義, Negotiate和Ntlm. 在 IIS Windows Authentication 能夠配置多個Provider. 默認狀況下有2個, 分別是Negotiate 和 NTLM.ui
namespace System.Net { /// <summary>Specifies protocols for authentication.</summary> [Flags] public enum AuthenticationSchemes { /// <summary>No authentication is allowed. A client requesting an <see cref="T:System.Net.HttpListener" /> object with this flag set will always receive a 403 Forbidden status. Use this flag when a resource should never be served to a client.</summary> None = 0, /// <summary>Specifies digest authentication.</summary> Digest = 1, /// <summary>Negotiates with the client to determine the authentication scheme. If both client and server support Kerberos, it is used; otherwise, NTLM is used.</summary> Negotiate = 2, /// <summary>Specifies NTLM authentication.</summary> Ntlm = 4, /// <summary>Specifies Windows authentication.</summary> IntegratedWindowsAuthentication = 6, /// <summary>Specifies basic authentication. </summary> Basic = 8, /// <summary>Specifies anonymous authentication.</summary> Anonymous = 32768 } }
在獲得這些線索以後, 檢查了Windows Authentication的Providers. 果真只有一個NTLM. 添加了一個新的Negotiate 的 Provider以後, WCF Service就獲得瞭解決.this
這篇文章中僅討論其中一種可能形成這樣問題的狀況. 這裏我遇到的問題與Windows Authentication的Provider有關係.spa
當 WCF Service 上啓用了Transport 層面上的安全設定以後, 能夠配置某一種類型的ClientCredentialType. 其中能夠包括 None, Basic, Digest, Ntlm, Windows, Certificate 和 Password.
當指定爲Windows的時候, 實質是要求Kerboer或者NTLM二者皆可. Server先去嘗試Kerberos驗證, 若是Kerberos驗證失敗, 則會嘗試經過NTLM. 也能夠經過設置另外的屬性 AllowNtlm爲false來強制使用Kerberos. Kerberos和Ntlm是兩種不一樣的驗證方式.
因爲這一點的不一樣, 在IIS上要確保作出相應的配置. 即, 開啓IIS的Windows Authentication的同時, 要確保Negotiate Provider也在列表中. 只有Ntlm會被認爲是錯誤的配置.
能夠參考這裏的連接 :
Sonic Guo