HttpPlatformHandler 通过启用 web.config 中的 forwardWindowsAuthToken 设置来支持转发身份验证令牌。 当需要使用 Windows 集成身份验证时,这听起来像是一个有用的功能。 文档对此非常模糊,没有解释如何使用此令牌来获取经过身份验证的用户名。
如果此设置设置为 true,令牌将被转发到 子进程以 %HTTP_PLATFORM_PORT% 作为标头侦听 每个请求的“X-IIS-WindowsAuthToken”。这是它的责任 处理每个请求对此令牌调用 CloseHandle。默认值 价值是假的。
在我的用例中,我需要将 Windows 集成身份验证与 Python 结合使用,因此使用 IIS 前端进行设置并使用 HTTP 平台处理程序将请求转发到 Python。
问题是,如何从 Python 中提供的令牌中获取用户名? “X-IIS-WindowsAuthToken”标头中的令牌看起来像 22b 一样的 3 字符十六进制。
好的,所以我对此进行了一些研究,最后回顾了Microsoft.AspNetCore.Server.IISIntegrateion.AuthenticationHandler 是如何做到的。
然后在想出一种方法之后,我想发布这个答案,这样 1) 我可以稍后找到它,2) 至少它在 SO 上以防万一其他人想知道。
好的,所以十六进制值是句柄,我们可以使用句柄调用模拟用户然后获取用户名,完成。
您只需要 pywin32 包:
pip install pywin32
Python 中的完整示例:
import win32api
import win32security
if 'x-iis-windowsauthtoken' in request.headers.keys():
handle_str = request.headers['x-iis-windowsauthtoken']
handle = int(handle_str, 16) # need to convert from Hex / base 16
win32security.ImpersonateLoggedOnUser(handle)
user = win32api.GetUserName()
win32security.RevertToSelf() # undo impersonation
win32api.CloseHandle(handle) # don't leak resources, need to close the handle!
print(f"user name: {user}")
这篇文章对我很有用。有一件事我真的不知道冒充来获取用户信息是否是正确的方法。不过,我不是 Windows 专家。
所以我尝试了使用 GetTokenInformation 和 LookupAccountSid 的另一条路径。这次用的是我比较熟悉的ruby(ruby on rails)
我是第一次使用fiddle,但是我想说如果你对C稍有了解,fiddle并没有那么难。
我希望这篇文章能帮助在 Windows 平台上使用 ruby 的人。
require "fiddle/import"
require 'fiddle/types'
module WIN32Security
extend Fiddle::Importer
dlload 'advapi32.dll'
include Fiddle::Win32Types
extern 'BOOL GetTokenInformation(HANDLE, UINT, PVOID, DWORD, PDWORD)'
extern 'BOOL LookupAccountSidW(LPSTR, PVOID, LPSTR, PDWORD, LPSTR, PDWORD, PVOID)'
# c.f. https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-token_information_class
TokenUser = 1
end
module WIN32
extend Fiddle::Importer
dlload 'kernel32.dll'
include Fiddle::Win32Types
extern 'BOOL CloseHandle(HANDLE)'
end
module SelfServicesHelper
def authorize_by_authtoken
if request.headers.key? "HTTP_X_IIS_WINDOWSAUTHTOKEN"
handle = request.headers["HTTP_X_IIS_WINDOWSAUTHTOKEN"].hex
buflen = 64
tokenInfo, len = "\0" * buflen, [0].pack("L!")
if WIN32Security::GetTokenInformation(handle, WIN32Security::TokenUser, tokenInfo, buflen, len) != 0
namelen, dnamelen, use = *[32,32].map{|e| [e].pack("L!")}, [0].pack("I")
namebuf, dnamebuf = [namelen, dnamelen].map{|e| "\0".encode("utf-16le") * e.unpack1("L!")}
# ... PSID is at the top of tokenInfo
if WIN32Security::LookupAccountSidW(nil, tokenInfo.unpack1("Q!"), namebuf, namelen, dnamebuf, dnamelen, use) != 0
namelen, dnamelen = [namelen, dnamelen].map{|e| e.unpack1("L!")}
WIN32::CloseHandle(handle)
logger.debug {"namebuf: %s, dnamebuf: %s" % [namebuf[0, namelen].encode("utf-8"), dnamebuf[0, dnamelen].encode("utf-8")}
else
logger.error "LookupAccountSidW returned false, last error: %d" % Fiddle.win32_last_error
end
else
logger.error "GetTokenInformation returned false, last error: %d" % Fiddle.win32_last_error
end
else
logger.debug "no HTTP_X_IIS_WINDOWSAUTHTOKEN"
end
end
end