Blazor Server应用通过Graph API获取组织用户列表问题求助
解决Microsoft登录后获取组织所有用户的问题
问题根源
你当前代码里使用了客户端凭据流(AcquireTokenForClient),这是应用权限的认证方式,但你拥有的是委托权限(User.Read.All),需要使用代表当前登录用户的令牌调用Graph API。另外代码还存在几个错误:
GetAccessToken方法中请求的Scope是Application.Read.All,不是所需的User.Read.All- 方法最后返回
result.AccessToken,但实际变量是result22 - 手动创建HttpClient调用Graph的方式不规范,应该利用Microsoft.Identity.Web的集成能力简化流程
修复步骤
1. 配置Program.cs集成Microsoft Graph
修改Program.cs,添加Microsoft Graph服务注册,让框架自动处理令牌获取:
using Microsoft.Identity.Web.UI; using Microsoft.Identity.Web; using blazor.UI.Helper; using AntDesign; using Blazored.SessionStorage; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc.Authorization; namespace blazorserver.UI { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // 定义Graph所需权限 var graphScopes = new[] { "User.Read.All" }; // 添加认证并集成Microsoft Graph builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")) .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"), graphScopes); builder.Services.AddControllersWithViews() .AddMicrosoftIdentityUI(); builder.Services.AddServerSideBlazor() .AddMicrosoftIdentityConsentHandler(); // 其他服务注册保持不变 builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddHttpClient(); builder.Services.AddAntDesign(); builder.Services.AddBlazoredSessionStorage(); builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped<NotificationService>(); builder.Services.AddScoped<IStorage, LocalStorage>(); builder.Services.AddAuthorization(options => { options.FallbackPolicy = options.DefaultPolicy; }); var app = builder.Build(); app.UseCookiePolicy(new CookiePolicyOptions { Secure = CookieSecurePolicy.Always }); 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(); } } }
同时确保appsettings.json中添加MicrosoftGraph配置节点:
"MicrosoftGraph": { "BaseUrl": "https://graph.microsoft.com/v1.0", "Scopes": "User.Read.All" }
2. 修改登录页获取组织用户并存入Session
删除手动获取令牌的GetAccessToken方法,直接注入GraphServiceClient调用API:
@page "/" @page "/login" @layout LoginLayout @using Azure.Core; @using blazorServer.Shared.Models @using System; @using System.Text.Json; @using System.Net.Http.Json @using System.Collections.Generic; @using System.Collections.ObjectModel; @using BlazorServer.UI.Helper; @using Microsoft.Extensions.Configuration; @using System.Security.Claims @using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.WebUtilities; @using Microsoft.Extensions.Primitives; @using Microsoft.Graph; @using Azure.Identity; @using Microsoft.Identity.Web; @using System.Net.Http.Headers; @using Microsoft.Identity.Client; @using System.IdentityModel.Tokens.Jwt; @using System.Threading; @inject HttpClient Http @inject NavigationManager NavigationManager @inject IConfiguration configuration @inject IHttpClientFactory ClientFactory @inject Blazored.SessionStorage.ISessionStorageService sessionStorage @inject AuthenticationStateProvider AuthenticationStateProvider @inject IStorage LocalStorage @inject GraphServiceClient GraphClient <Spin Spinning=_loading Tip="Processing..."> <GridRow Align="center" Style="minHeight:100vh;height:100vh;overflow: ""> <GridCol Span="18"> <Image Preview=false Src="/loginscreen.jpg" Height="100%" Width="100%" /> </GridCol> <GridCol Xs="6" Style="background-color:#004a87;""> <GridRow Align="center"> <GridCol Span="24" Style="text-align:center;""> <Image Preview=false Src="/dd.svg" Alt="logo" Height="150px" Width="150px" /> <br /> <Button Loading="true"> <span style="color:black;font-size:larger">user authenticating...</span> </Button> </GridCol> </GridRow> <br /> <br /> <br /> </GridCol> </GridRow> </Spin> @code { string style = "background: #0092ff; padding: 0 0 0 0;"; public class Model { public string Username { get; set; } public string Password { get; set; } public bool WindowsLogin { get; set; } = true; } private Model model = new Model(); AntDesign.Form<Model> form; private bool auto = false; private string apiURL; private UserSession userSession; private bool _loading = false; private string returnUrl; private List<User> organizationUsers; protected override void OnInitialized() { apiURL = configuration.GetValue<string>("WebApiUrl"); var uri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri); StringValues values; if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("returnUrl", out values)) { returnUrl = Convert.ToString(values); } } protected override async Task OnAfterRenderAsync(bool firstRender) { try { _loading = true; var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var user = authState.User; if (user.Identity.IsAuthenticated) { // 获取组织所有用户 try { var userPage = await GraphClient.Users.Request().GetAsync(); organizationUsers = new List<User>(userPage.CurrentPage); // 处理分页获取剩余用户 while (userPage.NextPageRequest != null) { userPage = await userPage.NextPageRequest.GetAsync(); organizationUsers.AddRange(userPage.CurrentPage); } // 存入Session await sessionStorage.SetItemAsync("OrganizationUsers", organizationUsers); } catch (Exception graphEx) { Console.WriteLine($"获取组织用户失败: {graphEx.Message}"); } var userName = user.Identity.Name; var userEmail = user.FindFirst(ClaimTypes.Email)?.Value; var userCredentials = await Http.GetFromJsonAsync<List<UserModel>>(apiURL + "api/admin/getuserbyemail?email=" + user.Identity.Name); if (userCredentials != null && userCredentials.Count > 0) { userSession = new() { Name = userCredentials.First().FirstName + " " + userCredentials.First().LastName, Email = userCredentials.First().Email, UserId = userCredentials.First().FirstName + " " + userCredentials.First().LastName, FirstName = userCredentials.First().FirstName, UserRoles = userCredentials.Select(x => x.Role).ToList() }; if (userSession.UserId == null || userSession.UserId.Length <= 0) { userSession.UserId = userSession.Name.Contains("\\") ? userSession.Name.Split("\\")[1] : userSession.Name; } await sessionStorage.SetItemAsync("UserSession", userSession); NavigationManager.NavigateTo(string.IsNullOrWhiteSpace(returnUrl) ? "/WorkList" : $"/{returnUrl}", true); } else { userSession = new() { Name = user.Identity.Name, Email = user.FindFirst(c => c.Type == ClaimTypes.Email)?.Value, UserId = user.FindFirst(c => c.Type == ClaimTypes.WindowsAccountName)?.Value, FirstName = user.FindFirst(c => c.Type == ClaimTypes.Surname)?.Value }; if (userSession.UserId == null || userSession.UserId.Length <= 0) { userSession.UserId = userSession.Name.Contains("\\") ? userSession.Name.Split("\\")[1] : userSession.Name; } await sessionStorage.SetItemAsync("UserSession", userSession); NavigationManager.NavigateTo(string.IsNullOrWhiteSpace(returnUrl) ? "/dd" : $"/{returnUrl}", true); } } else { await DisplayNotification(NotificationType.Error, "Error", "The user is NOT authenticated"); } _loading = false; } catch(Exception ex) { Console.WriteLine(ex); throw ex; } } }
3. 权限验证
确保Azure AD应用注册中:
- 已添加
User.Read.All委托权限并完成管理员同意 - 重定向URI配置正确
- 客户端凭据(若需)配置无误
后续使用
在需要展示下拉框的组件中,从Session取出用户列表:
@inject Blazored.SessionStorage.ISessionStorageService sessionStorage private List<User> users; protected override async Task OnInitializedAsync() { users = await sessionStorage.GetItemAsync<List<User>>("OrganizationUsers"); }
内容的提问来源于stack exchange,提问作者Srinidhi S
相关产品推荐
相关产品推荐

