Blazor Server集成Microsoft Entra ID:用户数据获取与服务注册问题
解决Blazor Server注册请求级用户服务的错误问题
错误原因
你遇到的InvalidOperationException是因为没有在DI容器中注册IUser接口对应的实现类,同时缺少结合Microsoft Graph调用逻辑的服务封装。
解决步骤
1. 定义用户接口与实现类
在Data文件夹下创建IUser.cs和UserService.cs:
IUser.cs
namespace BlazorServerAppAzureAD.Data; public interface IUser { // 获取当前登录用户信息 Task<CurrentUser> GetCurrentUserAsync(); // 获取其他用户列表(可按需调整参数) Task<List<OtherUser>> GetOtherUsersAsync(); } // 封装当前用户数据结构 public class CurrentUser { public string DisplayName { get; set; } public string Id { get; set; } public string Email { get; set; } } // 封装其他用户数据结构 public class OtherUser { public string DisplayName { get; set; } public string Id { get; set; } public string Mail { get; set; } }
UserService.cs
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.Graph; using System.Security.Claims; namespace BlazorServerAppAzureAD.Data; public class UserService : IUser { private readonly GraphServiceClient _graphClient; private readonly AuthenticationStateProvider _authStateProvider; public UserService(GraphServiceClient graphClient, AuthenticationStateProvider authStateProvider) { _graphClient = graphClient; _authStateProvider = authStateProvider; } public async Task<CurrentUser> GetCurrentUserAsync() { var authState = await _authStateProvider.GetAuthenticationStateAsync(); var user = authState.User; // 调用Graph获取当前用户详细信息 var graphUser = await _graphClient.Me.Request() .Select(u => new { u.Id, u.DisplayName, u.Mail }) .GetAsync(); return new CurrentUser { Id = graphUser.Id, DisplayName = graphUser.DisplayName, Email = graphUser.Mail ?? user.FindFirstValue(ClaimTypes.Email) }; } public async Task<List<OtherUser>> GetOtherUsersAsync() { // 调用Graph获取其他用户列表,按需限制数量 var users = await _graphClient.Users.Request() .Select(u => new { u.Id, u.DisplayName, u.Mail }) .Top(50) .GetAsync(); return users.Select(u => new OtherUser { Id = u.Id, DisplayName = u.DisplayName, Mail = u.Mail }).ToList(); } }
2. 在Program.cs中注册服务
确保已配置Microsoft Entra ID认证和Graph服务,再添加请求级服务注册:
using BlazorServerAppAzureAD.Data; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.Identity.Web; using Microsoft.Identity.Web.UI; var builder = WebApplication.CreateBuilder(args); // 配置Microsoft Entra ID认证 builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi(new[] { "https://graph.microsoft.com/User.Read.All" }) .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph")) .AddInMemoryTokenCaches(); builder.Services.AddControllersWithViews() .AddMicrosoftIdentityUI(); builder.Services.AddAuthorization(options => { options.FallbackPolicy = options.DefaultPolicy; }); // 注册请求级(Scoped)的IUser服务 builder.Services.AddScoped<IUser, UserService>(); builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor() .AddMicrosoftIdentityConsentHandler(); var app = builder.Build(); // 中间件配置 if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); app.Run();
3. 在页面中注入使用
在Index.razor中注入IUser服务,替代CascadingParameter:
@page "/" @inject BlazorServerAppAzureAD.Data.IUser UserService <h1>当前登录用户信息</h1> @if (currentUser != null) { <p>姓名: @currentUser.DisplayName</p> <p>UID: @currentUser.Id</p> <p>邮箱: @currentUser.Email</p> } <h2>其他用户列表</h2> @if (otherUsers != null) { <ul> @foreach (var user in otherUsers) { <li>@user.DisplayName - @user.Mail</li> } </ul> } @code { private CurrentUser currentUser; private List<OtherUser> otherUsers; protected override async Task OnInitializedAsync() { currentUser = await UserService.GetCurrentUserAsync(); otherUsers = await UserService.GetOtherUsersAsync(); } }
4. 验证配置
- 确保
appsettings.json中的AzureAd和MicrosoftGraph配置正确:
{ "AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "你的租户域名", "TenantId": "你的租户ID", "ClientId": "你的应用ID", "CallbackPath": "/signin-oidc" }, "MicrosoftGraph": { "BaseUrl": "https://graph.microsoft.com/v1.0", "Scopes": "User.Read.All" } }
- 确认Entra ID中应用的
User.Read.All权限已获取管理员同意。
关键注意点
- Blazor Server中
Scoped服务对应请求级生命周期,符合需求。 - 必须通过
AuthenticationStateProvider获取用户认证状态,不能直接依赖HttpContext。 - 调用Graph时用
Select方法指定需要的字段,减少数据传输量。
内容的提问来源于stack exchange,提问作者sada
相关产品推荐
相关产品推荐

