好吧,看来这毕竟是可以做到的。我找到了以下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
}