Windows身份认证替代Microsoft Graph身份认证的可行性及架构适配咨询
看起来你现在正面临把依赖Microsoft Graph的Azure AD认证体系切换到Windows认证的需求,先给你一个明确的结论:Windows认证没法直接作为「开箱即用的替代方案」,因为两者的认证逻辑、用户数据获取方式完全不同,你需要从认证流程到数据获取层都做针对性调整。下面我结合你的分层ASP.NET Core架构,详细拆解差异点、适配方案和替代选项:
一、核心差异点(为什么不能直接替换)
先把最关键的不同点理清楚,帮你理解调整的必要性:
1. 认证机制本质完全不同
你当前用的是OAuth2.0/OpenID Connect 协议的Azure AD云认证:靠MSAL库获取访问令牌,再用令牌调用Microsoft Graph API拉取Azure AD的用户数据;
而Windows认证是基于NTLM/Kerberos的域内本地认证:依赖企业Windows域控制器验证用户身份,没有「令牌」这一套机制——身份验证是由应用服务器(IIS/Kestrel)直接和域控制器交互完成的,认证通过后直接拿到当前用户的Windows身份标识。
2. 用户数据获取路径天差地别
- 之前你通过
GraphServiceClient调用Microsoft Graph的REST API,跨网络拉取Azure AD的用户数据(包括全量用户列表); - Windows认证本身只负责「验证身份」,不会主动提供用户目录数据。要获取域用户的详细信息或全量列表,你得用Active Directory Services (AD DS) 的.NET类库(比如
System.DirectoryServices.AccountManagement)直接连接企业域控制器,而不是调用云API。
3. 权限控制逻辑不同
- Microsoft Graph靠「权限范围(Scope)」(比如
User.Read.All)控制对用户列表的访问,权限由Azure AD的应用注册配置; - Windows认证下访问域用户数据,靠的是运行应用的服务账号的域权限:比如你的API服务要读取域用户列表,必须确保运行API的账号(域账号)被域控制器授予了「读取用户对象」的权限。
4. 分层架构中的认证流程变化
你当前的流程是:Client层通过MSAL拿令牌 → 带令牌调用API → API消费Graph数据;
Windows认证下,流程会变成:
- 如果是Blazor Server/MVC客户端:直接通过Windows集成认证传递身份,Client层不需要处理令牌;
- 如果是Blazor WebAssembly客户端:WASM本身无法直接使用Kerberos/NTLM,必须通过API层代理身份——Client调用API时,由API层完成Windows认证,再返回用户数据。
二、适配Windows认证的具体方案(针对你的分层架构)
结合你的TestApp.Client/API/Domain分层结构,给你一套可落地的调整步骤:
1. 替换Client层的认证配置
情况1:Client是Blazor Server/MVC/Razor Pages
移除所有MSAL和Graph相关的配置,替换为Windows认证中间件:
// TestApp.Client/Program.cs // 移除原有的AddMsalAuthentication、AddGraphClient代码 builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate(); builder.Services.AddAuthorization(options => { // 设置默认授权策略,确保所有请求都需要认证 options.FallbackPolicy = options.DefaultPolicy; }); // 中间件顺序要对 app.UseAuthentication(); app.UseAuthorization();
情况2:Client是Blazor WebAssembly
WASM无法直接使用Windows认证,需要让API层负责认证,Client通过API代理获取数据:
- Client层可以用Cookie认证(如果是托管在同一域下),或者保持匿名但所有数据请求都转发到API,由API验证Windows身份。
2. 重构用户数据获取服务(替换GraphServiceClient)
把原来依赖GraphServiceClient的AdUserService拆分为「API层的AD访问服务」和「Client层的API调用服务」:
第一步:在API层实现AD域用户访问逻辑
先安装NuGet包:System.DirectoryServices.AccountManagement
// TestApp.API/Services/AdUserService.cs using System.DirectoryServices.AccountManagement; using TestApp.Application.Repositories; using TestApp.Shared.DTOs; namespace TestApp.API.Services; public class AdUserService : IAdUserService { private readonly PrincipalContext _domainContext; // 从配置文件读取域信息和服务账号 public AdUserService(IConfiguration configuration) { var domainName = configuration["ActiveDirectory:DomainName"]; var serviceUser = configuration["ActiveDirectory:ServiceAccount"]; var servicePwd = configuration["ActiveDirectory:ServicePassword"]; // 连接域控制器 _domainContext = new PrincipalContext(ContextType.Domain, domainName, serviceUser, servicePwd); } // 获取当前认证用户的详细信息 public async Task<AdUserDto> GetCurrentDomainUserAsync(string windowsUsername) { // Windows用户名格式为:DOMAIN\Username var userPrincipal = UserPrincipal.FindByIdentity(_domainContext, windowsUsername); if (userPrincipal == null) return null; return new AdUserDto { Id = userPrincipal.Sid.Value, DisplayName = userPrincipal.DisplayName, JobTitle = userPrincipal.Description, // 映射AD的JobTitle属性(根据你的AD配置调整) MobilePhone = userPrincipal.VoiceTelephoneNumber, Email = userPrincipal.EmailAddress, GivenName = userPrincipal.GivenName, Surname = userPrincipal.Surname }; } // 获取符合条件的域用户列表(匹配邮箱后缀+启用状态) public async Task<List<AdUserDto>> GetDomainUsersAsync() { var userList = new List<AdUserDto>(); // 构建AD查询条件 var searchFilter = new UserPrincipal(_domainContext) { Enabled = true, EmailAddress = "*@pasar.com.ph" }; using var searcher = new PrincipalSearcher(searchFilter); foreach (var result in searcher.FindAll()) { if (result is UserPrincipal user) { userList.Add(new AdUserDto { Id = user.Sid.Value, DisplayName = user.DisplayName, Email = user.EmailAddress, GivenName = user.GivenName, Surname = user.Surname }); } } return userList; } }
第二步:在API层暴露用户数据接口
// TestApp.API/Controllers/AdUserController.cs using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using TestApp.Shared.DTOs; namespace TestApp.API.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] // 只允许已认证的域用户访问 public class AdUserController : ControllerBase { private readonly IAdUserService _adUserService; public AdUserController(IAdUserService adUserService) { _adUserService = adUserService; } [HttpGet("current")] public async Task<IActionResult> GetCurrentUser() { // User.Identity.Name 就是当前域用户的身份标识(DOMAIN\Username) var user = await _adUserService.GetCurrentDomainUserAsync(User.Identity.Name); return Ok(user); } [HttpGet("list")] public async Task<IActionResult> GetUserList() { var users = await _adUserService.GetDomainUsersAsync(); return Ok(users); } }
第三步:重构Client层的AdUserService
移除GraphServiceClient依赖,改为调用API接口:
// TestApp.Client/Services/AdUserService.cs using TestApp.Client.Services.Interfaces; using TestApp.Shared.DTOs; using System.Net.Http; namespace TestApp.Client.Services; public class AdUserService(HttpClient httpClient, INotificationService notificationService) : IAdUserService { private readonly HttpClient _httpClient = httpClient; public async Task<AdUserDto> GetCurrentUserAsync() { try { return await _httpClient.GetFromJsonAsync<AdUserDto>("api/aduser/current"); } catch (Exception ex) { notificationService.ShowError($"获取当前用户失败:{ex.Message}"); return null; } } public async Task<List<AdUserDto>> GetAdUsersAsync2() { try { return await _httpClient.GetFromJsonAsync<List<AdUserDto>>("api/aduser/list"); } catch (Exception ex) { notificationService.ShowError($"获取用户列表失败:{ex.Message}"); return new List<AdUserDto>(); } } }
3. 配置API层的Windows认证
// TestApp.API/Program.cs builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate(); builder.Services.AddAuthorization(options => { options.FallbackPolicy = options.DefaultPolicy; }); // 中间件顺序不能错 app.UseAuthentication(); app.UseAuthorization();
三、如果Windows认证不适用,还有什么替代方案?
如果你的应用不是部署在Windows域环境(比如纯云部署),或者没法访问企业AD DS,可以考虑这些选项:
- Azure AD OIDC认证 + 自定义用户存储:继续用Azure AD认证(不用Microsoft Graph),只通过OIDC端点获取当前用户的基本信息,其他用户列表数据同步到自己的数据库(比如SQL Server)维护;
- Azure AD Domain Services (Azure AD DS):如果是云部署,用Azure AD DS把Azure AD的用户数据同步到云域控制器,然后用Windows认证或AD DS类库访问数据;
- 自定义ASP.NET Core Identity认证:完全脱离Azure AD,用自己的数据库存储用户数据,实现独立的认证体系——但需要维护用户数据的同步或录入。
四、关键注意事项
- 部署环境限制:Windows认证只能在Windows域环境下工作,如果你把应用部署在非Windows服务器(比如Linux),或者用户不是域用户,这个方案就不适用;
- Blazor WASM的特殊处理:WASM客户端无法直接使用Windows认证,必须通过API层代理所有用户数据请求;
- 权限配置:访问AD DS的服务账号必须有域的读取权限,否则会出现「无法访问域控制器」或「权限不足」的错误。
如果还有具体的场景细节(比如部署环境、Client类型),可以补充出来,我再帮你细化方案~




