WCF:如何使用Windows身份验证从线程内访问数据库?

时间:2013-12-11 11:24:45

标签: c# multithreading wcf iis windows-authentication

我们使用WCF服务(托管在IIS上,作为C#Silverlight项目的一部分,使用Windows身份验证登录)。

我想启动一个执行耗时处理的线程,它应该定期更新数据库。 “耗费时间”我的意思是至少几分钟,但总是不到一个小时。我不想冒着异步服务器调用超时的风险。

问题是我们使用Windows身份验证,一旦进程进入线程,它就会丢失上下文并因以下错误而失败:

  

用户'IIS APPPOOL \ DefaultAppPool'登录失败。

我尝试使用BackgroundWorker将数据库代码移至ProgressChanged()RunWorkerCompleted()调用,希望他们可以加入原始上下文,但没有运气。

3 个答案:

答案 0 :(得分:1)

在您的IIS中,右键单击应用程序池 - >高级设置并将应用程序池用户更改为可以访问数据库的Windows用户。

在IIS上运行的应用程序始终在您在其中定义的用户下运行。因此,您的WCF服务无法访问数据库,因为目前您的IIS池使用了用户" APPPOOL \ DefaultAppPool"那是本地用户。

答案 1 :(得分:1)

虽然您可以将应用程序池配置为作为可以访问数据库的特定域用户帐户运行,但请记住,IIS服务进程现在将以该用户身份运行。 (可选)您可以提取客户端服务凭据并使用WindowsImpersonationContext,使用客户端凭据建立数据库连接。

在您的服务中,您可以执行以下操作:

//open a SQL Server database connection using the client credentials 
using (var impcontext = ServiceSecurityContext.Current.WindowsIdentity.Impersonate())
{
    var dbConnection = new System.Data.SqlClient.SqlConnection("Data Source=myServer; Integrated Security=SSPI");
    dbConnection.Open();
    impcontext.Undo();
}

这假定您的WCF服务设置如下:

<system.serviceModel>
  <bindings>
    <basicHttpBinding>
      <binding name="basicHttpBindingConfiguration">
        <security mode="TransportCredentialOnly">
          <transport clientCredentialType="Windows" />
        </security>
      </binding>
    </basicHttpBinding>
  </bindings>
  <services>
    <service name="WcfService1.Service1">
      <endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicHttpBindingConfiguration" contract="WcfService1.IService1" />
    </service>
  </services>
  <behaviors/>
</system.serviceModel>

答案 2 :(得分:0)

基于我的discussion with codechurn,这对我有用:

问题是在我进入线程之前ServiceSecurityContext.Current被正确设置,然后它是NULL。他敦促我不要将它作为该主题的论据传递,即使它有效。

在搜索other answers时,我找到了可行的方法。 Thread.CurrentPrincipal.Identity设置为当前标识,可以是WindowsIdentity或另一个通用标识。即使您不使用Windows身份验证,下面的解决方案仍然有效,在这种情况下无需模拟。

new Thread(Run).Start(...);

void Run(object args)
{
    WindowsImpersonationContext impersonationContext = null;
    if (Thread.CurrentPrincipal.Identity is WindowsIdentity)
    {
        impersonationContext = (Thread.CurrentPrincipal.Identity as WindowsIdentity).Impersonate();
    }

    try
    {
        // access the DB as normal
    }
    catch { /* always handle exceptions in threads */ }
    finally
    {
        if (impersonationContext != null)
        {
            impersonationContext.Undo();
        }
    }
}