PowerShell通过Windows身份验证模拟其他用户连接SQL Server的方案问询
PowerShell模拟Windows用户使用Windows身份验证连接SQL Server的可行方案
实现的核心逻辑是切换代码执行时的Windows安全上下文,再走常规的Windows身份验证连接逻辑即可,目前有两种成熟的落地方式:
- 方案1:以目标用户身份启动独立PowerShell进程执行连接逻辑
该方案兼容性最高,不需要当前账号持有特殊权限,只要目标Windows账号本身有SQL Server的访问权限即可使用。
示例代码:
# 第一步:获取要模拟的Windows用户凭证 $targetWindowsCred = Get-Credential -Message "输入目标Windows账号,格式为 域名\用户名 或 计算机名\用户名" # 第二步:定义要执行的SQL操作脚本块 $sqlExecuteLogic = { $SQLServer = "替换为你的SQL Server实例地址" $Database = "替换为目标数据库名" # 直接调用原生Windows身份验证的连接逻辑,会自动继承当前进程的Windows身份 Invoke-Sqlcmd -ServerInstance $SQLServer -Database $Database -Query "SELECT SYSTEM_USER AS 实际登录SQL的账号" } # 第三步:以目标用户身份启动PowerShell进程执行SQL操作 Start-Process powershell.exe -Credential $targetWindowsCred -ArgumentList "-Command & {$sqlExecuteLogic}" -NoNewWindow -Wait
- 方案2:当前进程内调用Windows API模拟用户身份
该方案适合需要和当前脚本其他逻辑联动、不想启动独立进程的场景,要求当前执行PowerShell的账号拥有「身份模拟特权(SeImpersonatePrivilege)」。
示例代码:
# 引入Windows身份验证所需的系统API Add-Type @" using System; using System.Runtime.InteropServices; using System.Security.Principal; public class Win32Auth { [DllImport("advapi32.dll", SetLastError = true)] public static extern bool LogonUser(string userName, string domain, string password, int logonType, int logonProvider, out IntPtr token); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern bool CloseHandle(IntPtr handle); public const int LOGON32_LOGON_NETWORK = 3; public const int LOGON32_PROVIDER_DEFAULT = 0; } "@ # 配置目标用户信息 $domain = "替换为域名或本地计算机名" $userName = "替换为目标用户名" $password = ConvertTo-SecureString "替换为目标用户密码" -AsPlainText -Force $targetCred = New-Object System.Management.Automation.PSCredential("$domain\$userName", $password) $clearPwd = [System.Net.NetworkCredential]::new("", $targetCred.Password).Password # 登录获取用户身份令牌 $userToken = [IntPtr]::Zero $logonResult = [Win32Auth]::LogonUser($userName, $domain, $clearPwd, [Win32Auth]::LOGON32_LOGON_NETWORK, [Win32Auth]::LOGON32_PROVIDER_DEFAULT, [ref]$userToken) if (-not $logonResult) { throw "用户登录失败,错误码:$([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" } try { # 切换当前线程的身份上下文为目标用户 $windowsIdentity = New-Object System.Security.Principal.WindowsIdentity($userToken) $impersonateContext = $windowsIdentity.Impersonate() # 执行Windows身份验证的SQL连接,此时会使用模拟的用户身份 $SQLServer = "替换为你的SQL Server实例地址" $Database = "替换为目标数据库名" $conn = New-Object System.Data.SqlClient.SQLConnection $conn.ConnectionString = "Data Source=$SQLServer;Integrated Security=True;Initial Catalog=$Database" $conn.Open() Write-Host "实际登录SQL的账号:" $conn.ExecuteScalar("SELECT SYSTEM_USER") $conn.Close() } finally { # 清理资源,恢复为原始用户身份 if ($impersonateContext) { $impersonateContext.Undo() } if ($userToken -ne [IntPtr]::Zero) { [Win32Auth]::CloseHandle($userToken) } }
注意事项
- 你提到的
Invoke-Sqlcmd的-Credential参数确实仅支持SQL Server身份验证,无法直接传入Windows凭证 - 所有SQL Server的Windows身份验证逻辑都是自动读取当前线程/进程的Windows身份令牌,不需要额外传入凭证参数
- 跨域访问场景下,可以把
LOGON32_LOGON_NETWORK替换为LOGON32_LOGON_INTERACTIVE = 2提升兼容性
内容的提问来源于stack exchange,提问作者Tanaka Saito
相关产品推荐
相关产品推荐

