Blazor Server应用Azure AD登录事件处理及DI容器访问问题
实现方案说明
你选择的OnTokenValidated事件是完全符合需求的正确触发点,该事件仅会在用户首次登录、Azure AD令牌校验通过时触发,不会重复执行,刚好适配首次拉取用户数据的场景。
如何在事件处理程序中访问DI容器
你不需要用[FromServices]或者构造函数注入,TokenValidatedContext自带的HttpContext属性就包含当前请求的服务作用域容器,直接从中获取服务即可,这也是官方推荐的认证事件中访问DI的标准方式,避免直接使用根容器导致的服务生命周期混乱问题。
示例代码如下:
using Microsoft.Extensions.DependencyInjection; // 要引用这个命名空间才能用GetRequiredService方法 private async Task OnTokenValidatedFuncAsync(TokenValidatedContext context) { var userName = context.Principal.Identity.Name; // 从当前请求的服务作用域获取所需服务,替换成你自己的服务接口即可 var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>(); var cacheService = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>(); // 从数据库/API拉取用户数据 var userData = await userService.GetUserDataAsync(userName); var userId = context.Principal.FindFirstValue(System.Security.Claims.ClaimTypes.NameIdentifier); // 缓存逻辑二选一即可: // 方案1:小体量数据直接追加到用户声明中,后续Blazor中直接通过AuthenticationState读取 var extraClaims = new List<System.Security.Claims.Claim> { new("DisplayName", userData.DisplayName), new("Department", userData.Department) }; context.Principal.AddIdentity(new System.Security.Claims.ClaimsIdentity(extraClaims)); // 方案2:大体量数据存入缓存,用用户唯一标识作为Key,后续请求直接按Key读取 await cacheService.SetStringAsync($"UserData_{userId}", System.Text.Json.JsonSerializer.Serialize(userData), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24) }); await Task.CompletedTask.ConfigureAwait(false); }
备选纯Blazor生命周期实现方案
如果你不想修改认证中间件配置,也可以完全在Blazor的生命周期中实现,所有服务都可以直接注入,不需要处理DI访问问题:
- 先注册一个Scoped生命周期的用户状态容器
// Program.cs 中注册 builder.Services.AddScoped<UserCurrentState>(); // UserCurrentState类定义 public class UserCurrentState { public UserData MyData { get; set; } }
- 在根组件
App.razor的初始化生命周期中执行拉取逻辑
// App.razor代码后置 [Inject] private AuthenticationStateProvider AuthProvider { get; set; } [Inject] private IUserService UserService { get; set; } [Inject] private UserCurrentState CurrentState { get; set; } protected override async Task OnInitializedAsync() { var authState = await AuthProvider.GetAuthenticationStateAsync(); if (authState.User.Identity.IsAuthenticated && CurrentState.MyData == null) { var userId = authState.User.FindFirstValue(System.Security.Claims.ClaimTypes.NameIdentifier); CurrentState.MyData = await UserService.GetUserDataAsync(userId); } }
后续任意Blazor组件中注入UserCurrentState即可直接读取用户数据,每个用户会话只会执行一次拉取逻辑。
内容的提问来源于stack exchange,提问作者Matthew MacFarland
相关产品推荐
相关产品推荐

