禁用本地DC的Azure AD环境中,C#客户端获取UserPrincipalName的问题
解决Azure AD环境下获取登录用户UPN的.NET代码异常问题
问题分析
- 你遇到的
InvalidCastException,是因为在纯Azure AD(本地域控混合模式禁用)环境下,UserPrincipal.Current的底层实现存在兼容性问题,会误将用户主体识别为组主体,导致类型转换失败。 whoami.exe /upn能正常返回UPN,是因为它直接调用Windows原生的LSA(本地安全机构)API:先获取用户的安全标识符(SID),再通过SID查询对应的UPN,完全绕开了DirectoryServices.AccountManagement对本地域依赖的逻辑。
替代实现方案(模拟whoami逻辑)
可以直接调用Windows底层API实现相同效果,无需依赖DirectoryServices.AccountManagement,代码示例如下:
C# 代码实现
using System; using System.Runtime.InteropServices; using System.Security.Principal; public static string GetCurrentUserUpn() { IntPtr tokenHandle = IntPtr.Zero; try { // 获取当前进程的访问令牌 if (!OpenProcessToken(System.Diagnostics.Process.GetCurrentProcess().Handle, 0x0008 /*TOKEN_QUERY*/, out tokenHandle)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } // 获取令牌信息所需内存大小 uint tokenInfoSize = 0; GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, 0, out tokenInfoSize); if (Marshal.GetLastWin32Error() != 0x000000EA /*ERROR_INSUFFICIENT_BUFFER*/) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } // 分配内存存储令牌信息 IntPtr tokenInfoPtr = Marshal.AllocHGlobal((int)tokenInfoSize); try { if (!GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenUser, tokenInfoPtr, tokenInfoSize, out tokenInfoSize)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } // 解析出用户SID TOKEN_USER tokenUser = Marshal.PtrToStructure<TOKEN_USER>(tokenInfoPtr); SecurityIdentifier sid = new SecurityIdentifier(tokenUser.User.Sid); // 通过SID转换为NT账户格式 NTAccount ntAccount = (NTAccount)sid.Translate(typeof(NTAccount)); // 调用LookupAccountName获取UPN uint nameSize = 0; uint domainSize = 0; SID_NAME_USE sidNameUse = SID_NAME_USE.SidTypeUser; LookupAccountName(null, ntAccount.Value, IntPtr.Zero, ref nameSize, IntPtr.Zero, ref domainSize, out sidNameUse); if (Marshal.GetLastWin32Error() != 0x000000EA) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } IntPtr namePtr = Marshal.AllocHGlobal((int)nameSize); IntPtr domainPtr = Marshal.AllocHGlobal((int)domainSize); try { if (!LookupAccountName(null, ntAccount.Value, namePtr, ref nameSize, domainPtr, ref domainSize, out sidNameUse)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } return Marshal.PtrToStringUni(namePtr); } finally { Marshal.FreeHGlobal(namePtr); Marshal.FreeHGlobal(domainPtr); } } finally { Marshal.FreeHGlobal(tokenInfoPtr); } } finally { if (tokenHandle != IntPtr.Zero) { CloseHandle(tokenHandle); } } } // 定义Windows API相关结构体与枚举 [StructLayout(LayoutKind.Sequential)] private struct TOKEN_USER { public SID_AND_ATTRIBUTES User; } [StructLayout(LayoutKind.Sequential)] private struct SID_AND_ATTRIBUTES { public IntPtr Sid; public uint Attributes; } private enum TOKEN_INFORMATION_CLASS { TokenUser = 1 } private enum SID_NAME_USE { SidTypeUser = 1 } // 导入Windows API函数 [DllImport("advapi32.dll", SetLastError = true)] private static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool GetTokenInformation(IntPtr tokenHandle, TOKEN_INFORMATION_CLASS tokenInfoClass, IntPtr tokenInfo, uint tokenInfoLength, out uint returnLength); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern bool LookupAccountName(string lpSystemName, string lpAccountName, IntPtr lpSid, ref uint cchSid, IntPtr lpReferencedDomainName, ref uint cchReferencedDomainName, out SID_NAME_USE peUse); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject);
代码说明
- 这段代码完全复刻
whoami.exe /upn的执行逻辑:先获取当前进程的访问令牌,提取用户SID,再通过LookupAccountNameAPI查询该SID对应的UPN。 - 彻底规避了
DirectoryServices.AccountManagement在纯Azure AD环境下的兼容性问题,因为它直接调用Windows底层API,和whoami使用同一套机制。
为什么UserPrincipal.Current会报错?
DirectoryServices.AccountManagement命名空间最初是为本地Active Directory设计的,在纯Azure AD(无本地域控)的场景下,其内部逻辑无法正确解析Azure AD用户的主体类型,误将用户识别为组,从而抛出类型转换异常。
内容的提问来源于stack exchange,提问作者OGP
相关产品推荐
相关产品推荐

