我有两个应用程序,一个是 Windows 服务(用 .NET 6 编写),另一个是 Winform 应用程序(也是用 .NET 6 编写)。 Winform应用程序处于用户自动启动状态并缩减至系统托盘。 Windows 服务设置为自动启动。
Windows服务和Winform应用程序通过管道连接。现在我想访问我的 (asp.net core) api,它通过我的 Windows 服务使用 Windows 身份验证。为此,我认为我需要模仿。由于我不知道当前登录用户的凭据,我认为我可以简单地通过管道将
WindowsIdentity.Token
传递到我的服务。
像这样:
// ... other server pipe stuff ...
var writer = new StreamWriter(serverPipe) { AutoFlush = true };
await writer.WriteLineAsync(Convert.ToString(WindowsIdentity.GetCurrent().Token));
并在我的 Windows 服务中使用令牌,如下所示:
IntPtr.TryParse(Convert.ToString(TOKEN_FROM_WINFORM_APPLICATION), out var winAuthToken);
return await WindowsIdentity.RunImpersonatedAsync(new SafeAccessTokenHandle(winAuthToken), async () =>
{
using var client = new HttpClient(new HttpClientHandler
{
UseDefaultCredentials = true, // send winAuth token
});
var content = new StringContent(body, Encoding.UTF8, "application/json");
var url = $"{baseUrl}{endpoint}?{query}";
var response = requestMethod switch
{
RequestMethod.Get => await client.GetAsync(url),
RequestMethod.Post => await client.PostAsync(url, content),
_ => throw new ArgumentOutOfRangeException(nameof(requestMethod), requestMethod, null)
};
return await response.Content.ReadAsStringAsync();
});
此方法会导致
System.ArgumentException: "Invalid token for impersonation - it cannot be duplicated.
异常。
我也尝试过复制令牌,但仍然遇到相同的错误:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
IntPtr token = default;
if (DuplicateToken(WindowsIdentity.GetCurrent().Token, 3, ref token) != 0)
{
await writer.WriteLineAsync(Convert.ToString(token));
}
microsoft 文档以及我在 stackoverflow 上能找到的所有内容仅显示了如何使用客户端凭据进行模拟。
我还看到了一些使用 OpenProcessApi 的方法,但我不知道我需要哪个
ProcessHandle
和 DesiredAccess
。
如何使用当前登录用户的令牌在我的WebApi上执行Windows身份验证?
感谢您的帮助!
编辑:好吧,我发现(在这个答案中)我可以将
OpenProcessApi
与登录用户的processId一起使用来获得访问权限,但由于我已经运行了一个Winform应用程序,因此在那里获得它会更容易,不是吗?
我还发现使用
WindowsIndentity.Token
绝对是错误的,因为here指出它不能用作 userToken。
我在 Github 上发现了一个非常有用的类,它实现了 win32 api 方法WTSQueryUserToken,它正是我正在寻找的。它为当前登录的用户返回一个
AccessToken
。我只需要将 TokenType 切换为 TOKEN_TYPE.TokenImpersonation
。
现在我的代码如下所示(我将
ProcessExtensions.cs
重命名为 Win32Api.cs
):
var userToken = IntPtr.Zero;
Win32Api.GetSessionUserToken(ref userToken);
var identity = new WindowsIdentity(userToken);
// impersonate current logged in user
return await WindowsIdentity.RunImpersonatedAsync(identity.AccessToken, async () =>
{
using var client = new HttpClient(new HttpClientHandler
{
UseDefaultCredentials = true, // send winAuth token
});
var content = new StringContent(body, Encoding.UTF8, "application/json");
var url = $"{baseUrl}{endpoint}?{query}";
var response = requestMethod switch
{
RequestMethod.Get => await client.GetAsync(url),
RequestMethod.Post => await client.PostAsync(url, content),
_ => throw new ArgumentOutOfRangeException(nameof(requestMethod), requestMethod, null)
};
return await response.Content.ReadAsStringAsync();
});
这就像一个魅力!我希望这对未来的读者有所帮助。