如何无需用户认证编程获取AAD租户ID/域名以查询用户UPN
解决AAD加入设备上获取用户UPN的问题
我完全理解你在AAD加入的设备上调用UserPrincipal.FindByIdentity时遇到的困境——那个PrincipalServerDownException确实是因为AAD加入的设备并没有绑定传统的Active Directory域,直接用ContextType.Domain创建PrincipalContext自然找不到LDAP服务器。下面给你几个实用的解决思路,帮你顺利拿到用户的UPN:
1. 最直接的方式:从WindowsIdentity的声明中读取UPN
你之前误以为WindowsIdentity.Name不是UPN就放弃了这个方向,但其实Windows身份令牌里已经包含了UPN的声明信息,直接读取它就行,根本不用碰AD相关的API。代码示例:
public string GetUpnForLoggedOnUser() { var windowsIdentity = WindowsIdentity.GetCurrent(); // 查找UPN对应的声明 var upnClaim = windowsIdentity.Claims .FirstOrDefault(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"); return upnClaim?.Value ?? "无法获取UPN"; }
这个方法在AAD加入设备和域加入设备上都能正常工作,而且不需要依赖任何AD服务器连接,是最可靠的方案。
2. 如果一定要用UserPrincipal的方案
如果你坚持要使用UserPrincipal类,那首先得明确AAD租户的域名,然后用它来初始化PrincipalContext。获取租户信息的方式有两种:
- 从注册表读取:AAD加入的设备会在注册表
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\CDJ\TenantInfo下存储租户的详细信息,包括TenantId和TenantName(通常是xxx.onmicrosoft.com格式)。 - 通过系统API获取:可以使用
Windows.System.UserProfile.UserInformation.GetPrincipalNameAsync()(需要在UWP或桌面应用中启用对应的权限)。
拿到租户域名后,修改你的代码如下:
public string GetUpnForLoggedOnUser() { var windowsIdentity = WindowsIdentity.GetCurrent(); // 假设你已经通过注册表或其他方式拿到了租户域名 string tenantDomain = "your-tenant-name.onmicrosoft.com"; using (var principalContext = new PrincipalContext(ContextType.Domain, tenantDomain)) { // 明确指定用UPN来查找用户,避免格式歧义 var userPrincipal = UserPrincipal.FindByIdentity( principalContext, IdentityType.UserPrincipalName, windowsIdentity.Name); return userPrincipal?.UserPrincipalName ?? "无法获取UPN"; } }
不过要注意,这种方式需要设备能访问AAD的LDAP服务,而且你得确保租户域名的正确性,相比第一种方案要麻烦一些。
为什么你的原有代码会报错?
简单解释下你遇到的问题根源:
- 域加入的设备会自动绑定本地AD域,无参数的
PrincipalContext(ContextType.Domain)会自动连接到域控制器的LDAP服务器,所以能正常工作。 - 但AAD加入的设备并没有本地域的绑定,也没有可用的LDAP服务器,所以会抛出
LDAP server is unavailable的异常。 - 另外
WindowsIdentity.Name的格式是DOMAIN\Username,并不是UPN,用它来查找UserPrincipal本身就容易出错。
内容的提问来源于stack exchange,提问作者Martin K
相关产品推荐
相关产品推荐

