9

几天前我问了一个问题(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 用户,也无法对权限或模拟进行任何更改,也无法使用 runas。

我知道我可以编写可以连接到 SQL Server 数据库的 Perl 和 Java 应用程序,只使用以下四个参数:服务器名称、数据库名称、用户名(格式为域\用户)和密码。

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

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

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

感谢您的帮助,马丁

4

7 回答 7

9

我有一个类似的问题,我正在编写一个工具,该工具需要在一个域的机器上运行,并使用受信任的连接在另一个域的 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 命令使调试应用程序变得非常困难,而且味道也不好。

最终,我在代码项目中找到了这篇文章,该文章讨论了通过身份验证来操作 Active Directory,这是进行模拟的主要类:

    使用系统;
    使用 System.Runtime.InteropServices;// DLL导入
    使用 System.Security.Principal;// WindowsImpersonationContext

    命名空间 TestApp
    {
        类模仿者
        {
            // 组类型枚举
            枚举 SECURITY_IMPERSONATION_LEVEL : int
            {
                安全匿名 = 0,
                安全识别 = 1,
                安全模拟 = 2,
                安全委托 = 3
            }

            // 获取用户令牌
            [DllImport("advapi32.dll", SetLastError = true)]
            静态外部布尔登录用户(字符串 pszUsername,字符串 pszDomain,字符串 pszPassword,
                int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

            // 关闭由 LogonUser 返回的张开的手
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            extern static bool CloseHandle(IntPtr 句柄);

            // 创建重复的令牌句柄
            [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
                int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

            WindowsImpersonationContext 新用户;

            ///
            /// 尝试模拟用户。如果成功,返回
            /// 新用户身份的 WindowsImpersonationContext。
            ///
            /// 要模拟的用户名
            /// 登录域
            /// 用户登录密码
            ///
            public Impersonator(字符串 sUsername,字符串 sDomain,字符串 sPassword)
            {
                // 初始化令牌
                IntPtr pExistingTokenHandle = new IntPtr(0);
                IntPtr pDuplicateTokenHandle = new IntPtr(0);
                pExistingTokenHandle = IntPtr.Zero;
                pDuplicateTokenHandle = IntPtr.Zero;

                // 如果域名为空,则假定为本地机器
                if (sDomain == "")
                    sDomain = System.Environment.MachineName;

                尝试
                {
                    常量 int LOGON32_PROVIDER_DEFAULT = 0;

                    // 创建令牌
                    // 常量 int LOGON32_LOGON_INTERACTIVE = 2;
                    常量 int LOGON32_LOGON_NEW_CREDENTIALS = 9;
                    //const int SecurityImpersonation = 2;

                    // 获取令牌句柄
                    bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
                        LOGON32_LOGON_NEW_CREDENTIALS,LOGON32_PROVIDER_DEFAULT,参考 pExistingTokenHandle);

                    // 模拟失败了吗?
                    if (false == bImpersonated)
                    {
                        int nErrorCode = Marshal.GetLastWin32Error();

                        // 显示 LogonUser 失败的原因
                        throw new ApplicationException("LogonUser() 失败,错误代码:" + nErrorCode);
                    }

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

                    // DuplicateToken 失败了吗?
                    如果(假 == bRetVal)
                    {
                        int nErrorCode = Marshal.GetLastWin32Error();
                        关闭句柄(pExistingTokenHandle);// 关闭现有句柄

                        // 显示 DuplicateToken 失败的原因
                        throw new ApplicationException("DuplicateToken() 失败,错误代码:" + nErrorCode);
                    }
                    别的
                    {
                        // 使用新的主令牌创建新的身份
                        WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle);
                        WindowsImpersonationContext impersonatedUser = newId.Impersonate();

                        新用户 = 模拟用户;
                    }
                }
                最后
                {
                    //关闭句柄
                    if (pExistingTokenHandle != IntPtr.Zero)
                        关闭句柄(pExistingTokenHandle);
                    if (pDuplicateTokenHandle != IntPtr.Zero)
                        关闭句柄(pDuplicateTokenHandle);
                }
            }

            公共无效撤消()
            {
                新用户.Undo();
            }
        }
    }

要使用它:

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

//Connect to and use SQL server

impersonator.Undo();

我在 Undo 方法中添加,否则模拟对象往往会被垃圾收集。我还更改了代码以使用 LOGON32_LOGON_NEW_CREDENTIALS ,但这是一个戳并运行以使其工作;我仍然需要完全了解它的作用,我感觉它与 runas 上的 /netonly 标志相同。我还将稍微分解构造函数。

于 2011-01-19T14:39:59.273 回答
4

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

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

为了使 Windows 身份验证起作用,您必须选择以下选项之一:

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

如果您不能让客户端主机信任服务器域,也不能添加 NTLM 镜像帐户,并且 SQL Server 管理员足够清醒,不会启用 ANONYMOUS,那么您将无法连接。

于 2010-05-12T20:34:02.980 回答
2

您必须配置 SQL Server以允许 SQL Server 身份验证,即使用用户名和密码进行身份验证。

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

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

于 2010-05-12T20:22:33.463 回答
2

正如您所说的那样,Linux 机器上的 JDBC 或 Perl 都可以使用 Windows 身份验证和不同于当前登录用户的凭据连接到 SQL Server。顺便说一下,Windows CE 设备也是如此

我认为这不是 C# 的问题,而是 SQL Server OLE DB 驱动程序的问题。我猜上面提到的方法在网络级别“伪装成使用某些特定凭据的 Windows 机器”;SQL Server OLE DB 驱动程序缺少的一项功能。因此,我的建议是寻找可以访问 SQL Server 数据库的替代(可能是商业的?)OLE DB 驱动程序。不过,我不确定这样的事情是否存在。

于 2010-08-22T07:47:28.270 回答
0

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

为了回答 Remus 的建议,我不能做他建议的这三件事中的任何一件,但 Java 和 Perl 应用程序能够连接起来。还有其他想法吗?

谢谢,马丁

于 2010-05-12T20:52:52.903 回答
0

是否可以提示输入凭据

于 2010-05-12T21:11:17.697 回答
0

这是我使用 jTDS JDBC 驱动程序从非域机器连接的示例代码:

Class.forName("net.sourceforge.jtds.jdbc.Driver").newInstance(); 字符串 url = "jdbc:jtds:sqlserver://server/database;domain=domain"; conn = DriverManager.getConnection(url, "user", "password");

于 2010-05-12T22:23:15.827 回答