从WPF应用程序对不受信任的域进行身份验证

时间:2011-06-15 16:30:15

标签: sql-server wpf security active-directory

我有一个需要访问SQL Server 2008 R2数据库的WPF应用程序。使用活动目录保护数据库。应用程序在连接到域的工作站上运行正常,并且用户已成功登录。

我现在需要能够在连接到不同域的工作站上运行相同的应用程序。不幸的是,这个新域名由SBS控制,因此我无法在它们之间建立信任关系。

实际上,我(我认为)需要的是允许用户输入在托管数据库服务器的域(用户名和密码)上设置的凭据,然后在连接到数据库时模拟该用户的工具服务器

我已尝试使用LogonUser,LOGON32_PROVIDER_DEFAULT& LOGON32_LOGON_INTERACTIVE但这似乎没有做我想要的 - 而是抱怨我无法登录,因为我使用的工作站没有域帐户。

有人有任何其他建议吗?

3 个答案:

答案 0 :(得分:1)

设置当前电流

The database is secured using active directory

我认为您的意思是您的SQL连接使用的是Windows身份验证。无论哪种方式,还有一点比你意识到的更多。如果您使用“活动目录”进行身份验证身份,我敢打赌您依靠Kerberos进行Windows身份验证(这不仅仅是您的身份验证类型,还包括您的凭据类型)。我链接的文章解释了Kerberos和NTLM for SQL Server 2005之间的区别,但它与2008 R2相同。

遗憾的是,如果没有域之间的信任关系,如果您成功模拟托管数据库的域中的用户并不重要,您将无法连接(正如您所见)。 您需要这种信任关系

如果您确实设法在域之间建立信任关系,我已经发布了如何使用域组here完成SQL Server的跨域Windows身份验证,这可能对您有用。

替代设置

如果您不想使用SQL身份验证(我不喜欢使用我的应用程序打包凭据),我建议您将数据库操作分成更加面向服务的体系结构。因此,正在进行的实际SQL工作将进入WCF服务(与您的数据库托管在同一个域中并模拟服务标识),您的应用程序只需要请求该服务。然后,您可以使用NTLM保护您的Web服务仍使用Windows身份验证。这样,您仍然可以验证征求用户的身份,并依靠您自己的基本安全结构(即简单表)来授权使用。

请告诉我这是否对您没有意义,或者您是否需要进一步澄清。

答案 1 :(得分:0)

我确实有一个建议......但我认为你不会喜欢它。

使用SQL身份验证而不是集成。

我有一个类似的问题,经过一段时间的努力 - 事实证明,确实没有一种方法可以实现它。 See here for my post on dba.stackexchange.com

如果使用SQL身份验证不是一个选项,那么“可能”仍然是管道磁带/挽救线/完全黑客的方式来完成它但你可能不得不将问题迁移到服务器管理员论坛之一而不是StackOverflow - 因为它不再是一个编程问题。

答案 2 :(得分:0)

好吧,似乎也许它毕竟可以完成。我发现了以下link,这让我意识到我的主要问题是LogonUser API调用的LOGON32_LOGON_INTERACTIVE参数的使用不正确(它应该是LOGON32_LOGON_NEWCREDENTIALS)。

因此,我现在可以使用以下代码连接到SQL Server上的数据库,该数据库受Windows身份验证保护,但在与运行代码的工作站完全不相关的域上...

static void Main(string [] args){

SafeTokenHandle safeTokenHandle;

try {

    string userName = @"*****", domainName = @"*****", password = @"*****";
    bool returnValue = NativeMethods.LogonUser(userName, domainName, password, 
        NativeMethods.LogonType.NewCredentials, NativeMethods.LogonProvider.Default, out safeTokenHandle);

    if (false == returnValue) {
        int ret = Marshal.GetLastWin32Error();
        Console.WriteLine("LogonUser failed with error code : {0}", ret);
        throw new Win32Exception(ret);
    }

    using (safeTokenHandle) {

        WindowsIdentity windowsIdentity = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());
        using (WindowsImpersonationContext impersonationContext = windowsIdentity.Impersonate()) {

            using (DataTable table = new DataTable()) {
                using (SqlDataAdapter adapter = new SqlDataAdapter()) {
                    using (adapter.SelectCommand = new SqlCommand(@"select * from dbo.MyTable")) {
                        adapter.SelectCommand.CommandType = CommandType.Text;
                        using (adapter.SelectCommand.Connection = new SqlConnection(@"Data Source=Server;Initial Catalog=Database;Integrated Security=Yes")) {
                            adapter.SelectCommand.Connection.Open();
                            adapter.Fill(table);
                        }
                    }
                }

                Console.WriteLine(string.Format(@"{0} Rows retrieved.", table.Rows.Count));

            }

        }

    }

}
catch (Exception ex) {
    Console.WriteLine("Exception occurred. " + ex.Message);
}

当然,它需要整理,我需要提示用户提供他们的凭据(他们无法说服我使用硬编码凭证)但原则上它可行(除了匿名化)。

希望这能帮助别人一段时间。

哦,你还需要以下......

public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid {

    private SafeTokenHandle() : base(true) {
    }

    protected override bool ReleaseHandle() {
        return NativeMethods.CloseHandle(handle);
    }

}

[DllImport(@"kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(
    IntPtr handle);

[DllImport(@"advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(
    String lpszUsername,
    String lpszDomain,
    String lpszPassword,
    LogonType dwLogonType,
    LogonProvider dwLogonProvider,
    out SafeTokenHandle phToken);

public enum LogonType {
    Interactive = 2,
    Network = 3,
    Batch = 4,
    Service = 5,
    Unlock = 7,
    NetworkClearText = 8,
    NewCredentials = 9
}

public enum LogonProvider {
    Default = 0,
    WinNT35 = 1,
    WinNT40 = 2,
    WinNT50 = 3
}