代码之家  ›  专栏  ›  技术社区  ›  user304582

从非域计算机连接到域SQL Server 2005

  •  8
  • user304582  · 技术社区  · 14 年前

    几天前我问了一个问题( Access to SQL Server 2005 from a non-domain machine using Windows authentication )有一些有趣的,但不实用的建议。我想再问一次这个问题,但要明确我的限制是什么:

    我有一个Windows域,其中一台计算机正在运行SQL Server 2005,并且该域被配置为仅支持Windows身份验证。我希望在同一网络上但不在域中的计算机上运行C客户机应用程序,并访问SQL Server 2005实例上的数据库。

    我无法在任何一台计算机上创建或修改OS或SQL Server用户,也无法对权限或模拟进行任何更改,也无法使用runa。

    我知道我可以编写Perl和Java应用程序,这些应用程序只能使用这四个参数:服务器名称、数据库名称、用户名(表单域\用户)和密码。

    在C中,我尝试了各种方法:

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

    尝试将集成安全设置为“真”和“假”,但似乎没有任何效果。我想做的事情在C中是不可能的吗?

    谢谢你的帮助,马丁

    7 回复  |  直到 13 年前
        1
  •  2
  •   Heinzi    14 年前

    正如您正确地说的,Linux机器上的JDBC或Perl都可以连接到SQL服务器。 使用Windows身份验证 以及不同于当前登录用户的凭据。 The same is true for Windows CE devices 顺便说一下。

    我认为这不是C的问题,而是SQL Server OLE DB驱动程序的问题。我猜上面提到的方法“假装是使用某些特定凭据的Windows计算机”;这是SQL Server OLE DB驱动程序所缺少的一个特性。因此,我的建议是寻找一个替代方案(也许是商业的?)可以访问SQL Server数据库的OLE DB驱动程序。不过,我不确定这样的事情是否存在。

        2
  •  8
  •   Stephen Turner    14 年前

    我有一个类似的问题,我写了一个工具,需要在一个域上的计算机上运行,并使用可信连接在另一个域上的SQL服务器进行身份验证。我在这个问题上所能找到的一切都说不可能做到。相反,您必须加入域,使用SQL身份验证,参与一个名为kerberos的CHAP,或者让您的网络伙伴建立一个受信任的关系,以列举一些替代方案。

    问题是我知道我可以让它在某种程度上使用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();
    

    我在undo方法中添加了,否则模仿者对象倾向于被垃圾收集。我还修改了代码以使用logon32_logon_new_凭据,但这是一个poke和run,以使其工作;我仍然需要完全理解它的作用,我感觉它与run as上的/netonly标志相同。我还将对构造函数进行一点分解。

        3
  •  4
  •   Remus Rusanu    14 年前

    在连接字符串中指定用户名和密码是无用的,因为它们意味着SQL身份验证,并且您已经指定SQL Server只接受Windows身份验证。

    如果服务器不允许SQL身份验证,则 只有 连接的可能性是使用Windows身份验证,即。 IntegratedSecurity=true . 这意味着您的客户机将作为运行进程(或当前正在模拟的)的任何凭据进行身份验证。

    要使Windows身份验证工作,您必须选择以下选项之一:

    • 将未加入域的计算机加入域(它可以是自己的域!)它信任服务器域,然后将客户端进程作为域\用户凭据运行。
    • 使用NTLM镜像帐户:客户端和服务器上具有相同名称和密码的一对本地用户。
    • 以匿名方式授予对SQL Server的访问权限。

    如果您不能使客户机主机信任服务器域,也不能添加NTLM镜像帐户,并且SQL Server管理足够健全,无法启用匿名,那么您将无法连接。

        4
  •  2
  •   abatishchev Karl Johan    14 年前

    你必须 configure SQL Server 允许SQL Server身份验证,即使用用户名和密码进行身份验证。

    不能通过“类似”服务器身份验证的域用户名/密码进行身份验证,即直接指定域用户名/密码。

    当然,我可能是错的,但我确信这不是C或.NET的问题。如何在Perl或Java应用程序中登录SQLServer??

        5
  •  0
  •   user304582    14 年前

    我会给你一个我比较熟悉的Java回答:我使用JTDS JDBC驱动程序,上面提到了四个参数。我不太了解Perl应用程序,但它运行在Linux设备上,并且能够使用相同的参数进行连接。我无法将SQL服务器更改为支持SQL身份验证。

    要回答莱姆斯的建议,我不能做任何他建议的三件事,然而Java和Perl应用程序能够连接。还有其他想法吗?

    谢谢,马丁

        6
  •  0
  •   Chris Haas    14 年前

    是否可以选择 prompt for credentials ?

        7
  •  0
  •   user304582    14 年前

    下面是我使用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”);