【问题标题】:Is there a way to connect Windows C# Application to SQL Server using a Network Service Account有没有办法使用网络服务帐户将 Windows C# 应用程序连接到 SQL Server
【发布时间】:2018-04-13 03:55:40
【问题描述】:

我有一个 C# 应用程序,它使用 AD 组访问 SQL Server 数据库进行身份验证,但我被告知他们希望将其切换为使用与 Web 应用程序当前通过 Web 服务器应用程序池使用的相同的网络服务帐户.由于 Windows 应用程序不通过 Web 服务器和应用程序池,我不确定如何配置 Windows 应用程序以使用与 Web 应用程序相同的网络服务帐户。我搜索了 SO、MSDN 和 Google,但一无所获。有没有办法做到这一点?感谢您的帮助。

【问题讨论】:

    标签: c# .net sql-server active-directory


    【解决方案1】:

    我认为将 SQL 作为网络服务连接的唯一方法是将应用程序作为网络服务运行(我认为模拟不适用于此帐户)。假设我们谈论的是客户端应用程序(控制台、Windows 窗体或 WPF),我看到 3 个选项:

    1. 使用来自 SysInternals 的 PsExec.exe 将您的应用作为网络服务运行,如 here 所述。
    2. 将您的应用分为两部分 - 一个实现数据访问并作为网络服务运行的 Windows 服务和一个与该服务通信并实现 UI 和应用层的客户端应用
    3. 将您的数据访问实现为 Web 服务,以便它可以在网络服务凭据下的应用程序池中运行,并使您的应用程序使用该 Web 服务来访问数据。我认为这种方式在大多数情况下更可取,但可能需要进行认真的重构。

    【讨论】:

    • 感谢您提供的信息。 #1 看起来不起作用,因为它需要密码,因为 SQL 服务器上的帐户有密码,而负责的团队不会提供该密码。 #2和#3更有可能,但如果我是正确的,我仍然需要#2的密码,而#3不允许Web服务通过应用程序池身份验证。不幸的是,你是对的,这不是一件容易的事。
    • NetworkService 帐户没有密码。但我认为您需要提升权限才能按照描述使用 psexec 。无论如何,这可能是快速简便的临时解决方法,但不是永久解决方案,尤其是在用户的计算机上。有关 NetworkService 帐户的更多信息:msdn.microsoft.com/en-us/library/windows/desktop/…
    【解决方案2】:

    您将需要使用模拟。我们使用自定义类在我们的网络应用程序中使用它:

    Imports System.Web
    Imports System.Web.Security
    Imports System.Security.Principal
    Imports System.Runtime.InteropServices
    
    ''' <summary>Creates an impersonation session if needed</summary>
    ''' <remarks>
    '''        This is a clever little object, it is designed to facilitate the 'using' functionality to handle the initialisation and disposal of the impersonation
    '''        context.  It will only impersonate if the connection is set up to use windows authentication otherwise it will do nothing
    '''
    '''        Recommended usage
    '''            using new impersonate(domain, username, password)
    '''                Do database Stuff
    '''            end using
    '''    </remarks>
    Public Class Impersonate
        Implements IDisposable
    
    
    Private isImpersonating As Boolean = False
    
    Private Sub Impersonate(ByVal domain As String, ByVal username As String, ByVal password As String)
        If Me.impersonateValidUser(username, domain, password) Then
            ' all is well
        Else
            ' not so well, raise exception
            Throw New System.Exception("Unable to use provided AD authentication to connect to database")
        End If
    End Sub
    
    #Region "IDisposable Support"
        Private disposedValue As Boolean ' To detect redundant calls
    
        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    ' clean up the impersonation
                    undoImpersonation()
                End If
    
            End If
            Me.disposedValue = True
        End Sub
    
        ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
        'Protected Overrides Sub Finalize()
        '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        '    Dispose(False)
        '    MyBase.Finalize()
        'End Sub
    
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
    #End Region
    
    
    
    #Region "Impersonate"
    
    Dim LOGON32_LOGON_INTERACTIVE As Integer = 2
    Dim LOGON32_PROVIDER_DEFAULT As Integer = 0
    
    Dim impersonationContext As WindowsImpersonationContext
    
    Declare Function LogonUserA Lib "advapi32.dll" (ByVal lpszUsername As String, _
                            ByVal lpszDomain As String, _
                            ByVal lpszPassword As String, _
                            ByVal dwLogonType As Integer, _
                            ByVal dwLogonProvider As Integer, _
                            ByRef phToken As IntPtr) As Integer
    
    Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _
                            ByVal ExistingTokenHandle As IntPtr, _
                            ByVal ImpersonationLevel As Integer, _
                            ByRef DuplicateTokenHandle As IntPtr) As Integer
    
    Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long
    Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long
    
    
    Public Sub Page_Loada(ByVal s As Object, ByVal e As EventArgs)
        If impersonateValidUser("username", "domain", "password") Then
            'Insert your code that runs under the security context of a specific user here.
            undoImpersonation()
        Else
            'Your impersonation failed. Therefore, include a fail-safe mechanism here.
        End If
    End Sub
    
    Private Function impersonateValidUser(ByVal userName As String, _
    ByVal domain As String, ByVal password As String) As Boolean
    
        Dim tempWindowsIdentity As WindowsIdentity
        Dim token As IntPtr = IntPtr.Zero
        Dim tokenDuplicate As IntPtr = IntPtr.Zero
        impersonateValidUser = False
    
        If CType(RevertToSelf(), Boolean) Then
            If LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                         LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
                If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
                    tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
                    impersonationContext = tempWindowsIdentity.Impersonate()
                    If Not impersonationContext Is Nothing Then
                        impersonateValidUser = True
                    End If
                End If
            End If
        End If
        If Not tokenDuplicate.Equals(IntPtr.Zero) Then
            CloseHandle(tokenDuplicate)
        End If
        If Not token.Equals(IntPtr.Zero) Then
            CloseHandle(token)
        End If
    End Function
    
    Private Sub undoImpersonation()
        If Me.isImpersonating Then
            impersonationContext.Undo()
        End If
    End Sub
    
    #End Region
    End Class
    

    然后您可以使用如下代码调用该类:

    using new impersonate(domain, username, password)
       ' Do database Stuff
    end using
    

    【讨论】:

    • 不确定模拟是否可行。我之前调查过,发现最大的问题,至少在我们的案例中,是数据库团队不会向我们提供网络服务帐户的密码。虽然这是一个很好的建议,并且喜欢它背后的想法,但没有办法绕过这个限制。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多