【问题标题】:WCF: How do I access a Database from within a Thread using Windows Authentication?WCF:如何使用 Windows 身份验证从线程内访问数据库?
【发布时间】:2013-12-11 11:24:44
【问题描述】:

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

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

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

用户“IIS APPPOOL\DefaultAppPool”登录失败。

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

【问题讨论】:

    标签: c# multithreading wcf iis windows-authentication


    【解决方案1】:

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

    在 IIS 上运行的应用程序始终在您在那里定义的用户下运行。因此,您的 WCF 服务将无法访问数据库,因为此时您的 IIS 池使用的是本地用户“APPPOOL\DefaultAppPool”。

    【讨论】:

    • 我们在每个用户的数据库中设置了访问权限。我不希望数据库使用错误的用户。可以这样吗?
    • 如果调用wcf服务,服务器端的代码会一直在应用池使用的用户下执行。我 100% 确定,您不能将调用 Windows 用户传递给数据库。
    • 我们的 web.config 指定它必须使用客户端的 Windows 凭据,而不是 IIS 中的凭据。您的答案适用于失败的特殊情况。谢谢。
    【解决方案2】:

    虽然您可以将应用程序池配置为作为有权访问数据库的特定域用户帐户运行,但请记住,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>
    

    【讨论】:

    • 这实际上是我正在寻找的,但问题是一旦我进入线程,ServiceSecurityContext.Current 就是 NULL。
    • @PeetBrits 您是否在 IIS 中对托管 WCF 服务的虚拟目录启用了匿名访问?客户端是否配置为发送客户端凭据?我的猜测是您可能启用了匿名访问和 null ServuceSecurityContext。
    • ServiceSecurityContext 在线程内仅为 NULL。我想我可以将它作为参数传递...
    • 你不应该通过ServiceSecurityContext;相反,WCF 在创建服务实例时会为您设置它。如果为 null,则客户端未通过身份验证,正如我之前提到的,确保在 IIS 中关闭对 WCF 服务的匿名访问并且正确配置了 Windows 身份验证。听起来这是一个配置问题。有关ServiceSecurityContext 的更多信息,请参阅this MSDN page
    • 我试过了,但是您建议的更改对任何事情都没有影响。 ServiceSecurityContext 已设置并正常工作(默认情况下,我的 Windows 身份验证配置正确),但是当我进入自定义线程(我从 WCF 服务调用中创建)时,它为 NULL。您能否确认您的一项服务中自定义线程的这种行为?
    【解决方案3】:

    根据我的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();
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多