2010-05-12 72 views
8

前幾天我問了一個問題(Access to SQL Server 2005 from a non-domain machine using Windows authentication),這個問題得到了一些有趣但不可用的建議。我想再次提出這個問題,但要明確我的約束是什麼:從非域名機器連接到域SQL Server 2005

我有一個Windows域,其中一臺機器運行SQL Server 2005,並且配置爲僅支持Windows身份驗證。我想在同一網絡上的一臺機器上運行C#客戶機應用程序,但不在該域上,並訪問SQL Server 2005實例上的數據庫。

我無法在任何一臺機器上創建或修改操作系統或SQL Server用戶,也無法對權限或模擬做任何更改,也無法使用runas。

我知道我可以編寫只能使用以下四個參數連接到SQL Server數據庫的Perl和Java應用程序:服務器名稱,數據庫名稱,用戶名(以domain \ user形式)和密碼。

在C#我身邊有嘗試過各種東西:

string connectionString = "Data Source=server;Initial Catalog=database;User Id=domain\user;Password=password"; 
SqlConnection connection = new SqlConnection(connectionString); 
connection.Open(); 

,並試圖集成安全級別設置爲真與假,但似乎沒有任何工作。我在C#中試圖做什麼根本不可能?

感謝您的幫助,馬丁

回答

2

正如您所說的,Linux機器上的JDBC或Perl都可以使用Windows身份驗證和與當前登錄用戶不同的憑證連接到SQL Server 。順便說一句,The same is true for Windows CE devices

我認爲這不是C#的問題,而是SQL Server OLE DB驅動程序的問題。我猜想上面提到的方法「在網絡級別假裝成使用某些特定憑證的Windows機器」一個功能,SQL Server OLE DB驅動程序缺少。因此,我的建議是尋找可以訪問SQL Server數據庫的替代(可能是商業?)OLE DB驅動程序。不過,我不確定這樣的事情是否存在。

2

你必須configure SQL Server允許SQL Server身份驗證,即驗證使用的用戶名和密碼。

您無法通過域名用戶名/密碼「like」服務器身份驗證進行身份驗證,即直接指定域用戶名/密碼。

當然,我可能是錯的,但我確信這不是C#或.NET的問題。如何在Perl或Java應用程序中的SQL Server上登錄?

+0

實際上,這個問題很清楚地表明,使用SQL Server身份驗證不是一個選項(儘管...沒有downvote) – Heinzi 2010-08-22 07:49:21

4

無法在連接字符串中指定用戶名和密碼,因爲這些隱含了SQL身份驗證,並且您已指定SQL Server只接受Windows身份驗證。

如果服務器不允許SQL身份驗證,那麼只有可能性連接是使用Windows身份驗證,即。 IntegratedSecurity=true。這意味着您的客戶端將驗證任何憑據正在運行該進程(或正在被模擬)。

爲了讓Windows身份驗證工作,你必須選擇下列之一:

  • 加入非加入域的計算機到一個域(!它可以是它自己的域)一個信任的服務器域,然後運行客戶端進程作爲域\用戶憑證。
  • 使用NTLM鏡像帳戶:客戶端和服務器上具有相同名稱和密碼的一對本地用戶。
  • 授予對SQL Server的匿名訪問權限。

如果您不能讓客戶端主機信任服務器域,也不能添加NTLM鏡像帳戶,並且SQL Server管理員足夠強大而不啓用ANONYMOUS,那麼您將無法連接。

+1

沒有downvote,但:該問題沒有要求介紹SQL Server訪問方法。它詢問爲什麼.net框架無法使用Windows身份驗證和替代憑證*連接到SQL Server,儘管其他客戶端技術(下文提到的JDBC驅動程序,Windows CE上的Perl SQL Server驅動程序,System.Data.SqlClient。 ..)允許你做這樣的事情(所以它不能成爲SQL Server的限制)。 – Heinzi 2010-08-22 07:51:50

+0

@ Heinzi Remus確實提到了目前正在冒充的憑據。司機可以做到這一點。 – 2011-06-14 12:07:32

0

我會給你我更熟悉的Java答案:我使用帶有上述四個參數的jTDS JDBC驅動程序。我所知道的Perl應用程序較少,但在Linux機器上運行,並且能夠使用相同的參數進行連接。我無法將SQL Server更改爲支持SQL身份驗證。爲了回答Remus的建議,我不能完成他建議的三件事情中的任何一件,但Java和Perl應用程序能夠連接。任何其他想法?

謝謝,馬丁

0

它是一個選項,以prompt for credentials

+0

不 - 至少在我看來你的意思是。連接憑證在C#應用程序啓動時讀入的配置文件中指定。 – user304582 2010-05-12 21:24:33

0

這裏是我用來從使用JTDS JDBC驅動非域機連接樣本代碼:

的Class.forName(「net.sourceforge.jtds.jdbc.Driver」)的newInstance() ; String url =「jdbc:jtds:sqlserver:// server/database; domain = domain」; conn = DriverManager.getConnection(url,「user」,「password」);

+0

你應該將這些東西添加到你的問題中(使用你的問題下方的「編輯」按鈕),而不是將它添加爲答案。 – Heinzi 2010-08-22 07:36:35

8

我有一個類似的問題,我正在編寫一個工具,需要在一個域上的計算機上運行,​​並使用可信連接與另一個域上的SQL服務器進行身份驗證。我所能找到的關於這個問題的一切都說不能做到。相反,您必須加入域名,使用SQL身份驗證,加入Kerberos的一些參與者,或者讓您的網絡人員建立一個可信賴的關係,以提供一些替代方案。

是我知道我能得到它使用RUNAS某些方面的工作,因爲我已經有SSMS證明了它的事情:

C:\WINDOWS\system32\runas.exe /netonly /savecred /user:megacorp\joe.bloggs "C:\Program Files\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE\SqlWb.exe" 

的/ netonly標誌讓我與本地憑據執行.exe和使用遠程憑證訪問網絡,無論如何,我從遠程服務器獲得了我期望的結果集。問題在於runas命令使調試應用程序變得非常困難,而且沒有良好的氣味。

最終我發現這篇文章上the code project這是談論驗證操作的Active Directory,這是確實的模擬主類:

 
    using System; 
    using System.Runtime.InteropServices; // DllImport 
    using System.Security.Principal; // WindowsImpersonationContext 

    namespace TestApp 
    { 
     class Impersonator 
     { 
      // group type enum 
      enum SECURITY_IMPERSONATION_LEVEL : int 
      { 
       SecurityAnonymous = 0, 
       SecurityIdentification = 1, 
       SecurityImpersonation = 2, 
       SecurityDelegation = 3 
      } 

      // obtains user token 
      [DllImport("advapi32.dll", SetLastError = true)] 
      static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, 
       int dwLogonType, int dwLogonProvider, ref IntPtr phToken); 

      // closes open handes returned by LogonUser 
      [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 
      extern static bool CloseHandle(IntPtr handle); 

      // creates duplicate token handle 
      [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
      extern static bool DuplicateToken(IntPtr ExistingTokenHandle, 
       int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle); 

      WindowsImpersonationContext newUser; 

      /// 
      /// Attempts to impersonate a user. If successful, returns 
      /// a WindowsImpersonationContext of the new users identity. 
      /// 
      /// Username you want to impersonate 
      /// Logon domain 
      /// User's password to logon with 
      /// 
      public Impersonator(string sUsername, string sDomain, string sPassword) 
      { 
       // initialize tokens 
       IntPtr pExistingTokenHandle = new IntPtr(0); 
       IntPtr pDuplicateTokenHandle = new IntPtr(0); 
       pExistingTokenHandle = IntPtr.Zero; 
       pDuplicateTokenHandle = IntPtr.Zero; 

       // if domain name was blank, assume local machine 
       if (sDomain == "") 
        sDomain = System.Environment.MachineName; 

       try 
       { 
        const int LOGON32_PROVIDER_DEFAULT = 0; 

        // create token 
        // const int LOGON32_LOGON_INTERACTIVE = 2; 
        const int LOGON32_LOGON_NEW_CREDENTIALS = 9; 
        //const int SecurityImpersonation = 2; 

        // get handle to token 
        bool bImpersonated = LogonUser(sUsername, sDomain, sPassword, 
         LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle); 

        // did impersonation fail? 
        if (false == bImpersonated) 
        { 
         int nErrorCode = Marshal.GetLastWin32Error(); 

         // show the reason why LogonUser failed 
         throw new ApplicationException("LogonUser() failed with error code: " + nErrorCode); 
        } 

        bool bRetVal = DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle); 

        // did DuplicateToken fail? 
        if (false == bRetVal) 
        { 
         int nErrorCode = Marshal.GetLastWin32Error(); 
         CloseHandle(pExistingTokenHandle); // close existing handle 

         // show the reason why DuplicateToken failed 
         throw new ApplicationException("DuplicateToken() failed with error code: " + nErrorCode); 
        } 
        else 
        { 
         // create new identity using new primary token 
         WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle); 
         WindowsImpersonationContext impersonatedUser = newId.Impersonate(); 

         newUser = impersonatedUser; 
        } 
       } 
       finally 
       { 
        // close handle(s) 
        if (pExistingTokenHandle != IntPtr.Zero) 
         CloseHandle(pExistingTokenHandle); 
        if (pDuplicateTokenHandle != IntPtr.Zero) 
         CloseHandle(pDuplicateTokenHandle); 
       } 
      } 

      public void Undo() 
      { 
       newUser.Undo(); 
      } 
     } 
    } 

只使用它:

Impersonator impersonator = new Impersonator("username", "domain", "password"); 

//Connect to and use SQL server 

impersonator.Undo(); 

我在撤消方法中添加了否則模擬器對象傾向於垃圾收集。我還修改了代碼以使用LOGON32_LOGON_NEW_CREDENTIALS,但這是一個捅捅並運行以使其工作;我仍然需要充分理解它的作用,我覺得它和runas上的/ netonly標誌是一樣的。我也打算分解一下構造函數。

+0

+1,很好的解決方案。關於垃圾收集問題:您可以將您的類重命名爲'ImpersonationContext',繼承'IDisposable'並在'Dispose'中執行'Undo'。因此,你的類可以如下使用:'使用新的ImpersonationContext(...){...}'。這也可以確保您不能「忘記」撤消模擬。 – Heinzi 2013-07-29 12:26:46