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

发布于 2024-09-01 03:47:08 字数 855 浏览 3 评论 0原文

我几天前问了一个问题(使用 Windows 身份验证从非域计算机访问 SQL Server 2005),得到了一些有趣但不可用的建议。我想再次问这个问题,但请明确我的限制:

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

我无法在任何一台计算机上创建或修改操作系统或 SQL Server 用户,并且无法对权限或模拟进行任何更改,并且无法使用 runas。

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

在 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# 中根本不可能实现吗?

感谢您的帮助,马丁

I asked a question a few days ago (Access to SQL Server 2005 from a non-domain machine using Windows authentication) which got some interesting, but not usable suggestions. I'd like to ask the question again, but make clear what my constraints are:

I have a Windows domain within which a machine is running SQL Server 2005 and which is configured to support only Windows authentication. I would like to run a C# client application on a machine on the same network, but which is NOT on the domain, and access a database on the SQL Server 2005 instance.

I CANNOT create or modify OS or SQL Server users on either machine, and I CANNOT make any changes to permissions or impersonation, and I CANNOT make use of runas.

I know that I can write Perl and Java applications that can connect to the SQL Server database using only these four parameters: server name, database name, username (in the form domain\user), and password.

In C# I have tried various things around:

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

and tried setting integrated security to true and false, but nothing seems to work. Is what I am trying to do simply impossible in C#?

Thanks for any help, Martin

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

一身骄傲 2024-09-08 03:47:08

我遇到了类似的问题,当时我正在编写一个工具,该工具需要在一个域的计算机上运行,​​并使用可信连接通过另一个域上的 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目录,这是执行模拟的主类:

    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_CREDENTIALS 但这是一个戳并运行才能使其工作;我仍然需要完全理解它的作用,我有一种感觉它与 runas 上的 /netonly 标志相同。我还将对构造函数进行一些分解。

I had a similar problem where I was writing a tool that needed to run on a machine on one domain and authenticate with a SQL server on another domain using a trusted connection. Everything I could find on the subject said it couldn't be done. Instead you must join the domain, use SQL authentication, get involved with some chap called Kerberos, or get your network guys to setup a trusted relationship, to name a few alternatives.

The thing is I knew I could get it working in some way using RUNAS because I'd proven it with 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"

The /netonly flag allowed me to execute the exe with the local credentials and access the network with the remote credentials, I think, anyway I got the result set I expected from the remote server. The problem was the runas command made it very difficult to debug the application, and it didn't smell good.

Eventually I found this article on the code project which was talking about authenticating to manipulate Active Directory, Here is the main class that does the impersonation:

    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();
            }
        }
    }

To use it just:

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

//Connect to and use SQL server

impersonator.Undo();

I added in the Undo method otherwise the impersonator object tended to get garbage collected. I also altered the code to use LOGON32_LOGON_NEW_CREDENTIALS but this was a poke and run to make it work; I still need to understand fully what it does, I have a feeling its the same as the /netonly flag on runas. I'm also going to break down the constructor a bit.

书信已泛黄 2024-09-08 03:47:08

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

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

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

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

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

Is useless to specify user name and password in connection string because those imply SQL Authentication, and you already specified that SQL Server only accepts Windows authentication.

If the server doesn't allow SQL Authentication then the only possibility to connect is to use Windows authentication, ie. IntegratedSecurity=true. Which means that your client will authenticate as whatever credential is running the process (or is being currently impersonated).

In order for Windows authentication to work, you have to choose one of the following:

  • Join the non-domain joined machine into a domain (it can be it's own domain!) that trusts the server domain, then run the client process as a domain\user credential.
  • Use NTLM mirrored accounts: a pair of local users on the client and the server with identical name and passwords.
  • Grant as ANONYMOUS access to the SQL Server.

If you cannot make the client host trust the server domain, nor can you add NTLM mirrored accounts, and the SQL Server admin is sane enough not to enable ANONYMOUS then you won't be able to connect.

口干舌燥 2024-09-08 03:47:08

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

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

As you correctly say, JDBC or Perl on a Linux machine can both connect to an SQL Server using Windows authentication and credentials which differ from the currently logged on user. The same is true for Windows CE devices, by the way.

I think that this is that this is not an issue of C# but of the SQL Server OLE DB driver. I guess the methods mentioned above "pretend to be a Windows machine using some specific credentials" on the network level; a feature, which the SQL Server OLE DB driver lacks. Thus, my suggestion would be to look for an alternative (maybe commercial?) OLE DB driver that can access SQL Server databases. I'm not sure if such a thing exists, though.

风向决定发型 2024-09-08 03:47:08

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

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

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

You have to configure SQL Server to allow SQL Server Authentication, i.e. authentication using username and password.

You can't authenticate by domain username/password 'like' server authentication, i.e. specify domain username/password directly.

I can be wrong of course, but I'm sure that this isn't a problem of C# or .NET. How can you login on SQL Server in your Perl or Java application??

原来是傀儡 2024-09-08 03:47:08

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

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

谢谢,马丁

I'll give you the Java answer which I'm more familiar with: I use the jTDS JDBC driver with the four parameters mentioned above. The Perl application I know less about, but is running on a Linux box, and is able to connect with the same parameters. I cannot change the SQL Server to support SQL Authentication.

To answer Remus' suggestions, I cannot do any of those three things he suggests and yet Java and Perl applications are able to connect. Any other ideas?

Thanks, Martin

暮凉 2024-09-08 03:47:08

是否可以选择提示输入凭据

Is it an option to prompt for credentials?

思慕 2024-09-08 03:47:08

以下是我使用 jTDS JDBC 驱动程序从非域计算机进行连接的示例代码:

Class.forName("net.sourceforge.jtds.jdbc.Driver").newInstance();
String url = "jdbc:jtds:sqlserver://服务器/数据库;域=域";
conn = DriverManager.getConnection(url, "用户", "密码");

Here is the sample code that I use to connect from a non-domain machine using the jTDS JDBC driver:

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文