You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server应用中登录用户信息缓存优化咨询:避免重复获取EmployeeId及存储复杂用户对象的实现方案

这确实是Blazor Server开发中很常见的性能优化痛点——每个页面初始化都去查数据库,不仅浪费资源,还会拖慢页面加载速度。结合Windows认证的场景,我给你几个实用的优化方案,按推荐度排序:

方案1:扩展AuthenticationStateProvider+内存缓存(最推荐)

Blazor Server本身依赖AuthenticationStateProvider管理用户身份,我们可以扩展这个类,在用户首次登录时一次性查询并缓存员工信息,后续所有组件直接从缓存或身份凭证中获取数据,完全避免重复查库。

实现步骤:

  1. 自定义AuthenticationStateProvider
    继承ServerAuthenticationStateProvider(适配Windows认证场景),重写身份获取逻辑,同时加入缓存和员工信息的Claims注入:
public class EmployeeAuthStateProvider : ServerAuthenticationStateProvider
{
    private readonly IEmployeeService _employeeService;
    private readonly IMemoryCache _cache;

    public EmployeeAuthStateProvider(IEmployeeService employeeService, IMemoryCache cache)
    {
        _employeeService = employeeService;
        _cache = cache;
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var baseAuthState = await base.GetAuthenticationStateAsync();
        var user = baseAuthState.User;

        if (user.Identity.IsAuthenticated)
        {
            var cacheKey = $"Employee_{user.Identity.Name}";
            // 优先从缓存取员工信息
            if (!_cache.TryGetValue(cacheKey, out Employee employee))
            {
                employee = await _employeeService.GetByShortName(user.Identity.Name);
                if (employee == null)
                {
                    // 返回未授权状态,后续组件可处理跳转
                    return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
                }

                // 将员工信息转为Claims,方便组件直接从User对象获取
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.NameIdentifier, employee.EmployeeId),
                    new Claim("FullName", employee.FullName)
                };
                // 批量添加角色Claims
                employee.Roles.ForEach(role => claims.Add(new Claim(ClaimTypes.Role, role.Name)));

                // 更新身份凭证
                var newIdentity = new ClaimsIdentity(user.Identity.AuthenticationType);
                newIdentity.AddClaims(claims);
                var updatedUser = new ClaimsPrincipal(newIdentity);

                // 缓存8小时(匹配会话有效期)
                _cache.Set(cacheKey, employee, TimeSpan.FromHours(8));
                return new AuthenticationState(updatedUser);
            }
            else
            {
                // 缓存已存在,直接构造带Claims的身份对象
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.NameIdentifier, employee.EmployeeId),
                    new Claim("FullName", employee.FullName)
                };
                employee.Roles.ForEach(role => claims.Add(new Claim(ClaimTypes.Role, role.Name)));

                var newIdentity = new ClaimsIdentity(user.Identity.AuthenticationType);
                newIdentity.AddClaims(claims);
                return new AuthenticationState(new ClaimsPrincipal(newIdentity));
            }
        }

        return baseAuthState;
    }

    // 提供便捷方法,直接获取完整员工对象
    public async Task<Employee> GetCurrentEmployeeAsync()
    {
        var authState = await GetAuthenticationStateAsync();
        var userName = authState.User.Identity.Name;
        return await _cache.GetOrCreateAsync($"Employee_{userName}", async entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(8);
            return await _employeeService.GetByShortName(userName);
        });
    }
}
  1. 注册服务
    在Program.cs中替换默认的身份提供者,并添加内存缓存:
builder.Services.AddScoped<AuthenticationStateProvider, EmployeeAuthStateProvider>();
builder.Services.AddMemoryCache();
  1. 组件中使用
    任何组件只需注入AuthenticationStateProvider,即可直接获取员工信息:
@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager NavigationManager

protected string EmployeeId { get; set; }
protected Employee CurrentEmployee { get; set; }

protected override async Task OnInitializedAsync()
{
    var authState = await AuthStateProvider.GetAuthenticationStateAsync();
    // 从Claims直接获取ID
    EmployeeId = authState.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    
    // 或者获取完整员工对象
    CurrentEmployee = await ((EmployeeAuthStateProvider)AuthStateProvider).GetCurrentEmployeeAsync();
    
    if (CurrentEmployee == null)
    {
        NavigationManager.NavigateTo("/accessdenied");
    }
}

优势:

  • 完全贴合Blazor身份系统设计,无需额外依赖
  • 仅首次加载时查一次数据库,后续全程用缓存
  • 员工信息可通过User对象的Claims全局访问,代码更简洁
方案2:Scoped全局用户上下文服务

创建一个Scoped生命周期的UserContext服务,在用户会话初始化时加载员工信息,所有组件直接注入该服务使用。

实现步骤:

  1. 定义UserContext类
public class UserContext
{
    public Employee CurrentEmployee { get; set; }
    public string EmployeeId => CurrentEmployee?.EmployeeId;
    public List<Role> Roles => CurrentEmployee?.Roles ?? new List<Role>();
}
  1. 注册服务
builder.Services.AddScoped<UserContext>();
  1. 初始化上下文
    在根组件(如App.razor)的初始化方法中加载员工信息:
@inject UserContext UserContext
@inject IEmployeeService EmployeeService
@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager NavigationManager

protected override async Task OnInitializedAsync()
{
    var authState = await AuthStateProvider.GetAuthenticationStateAsync();
    var userName = authState.User.Identity.Name;
    
    UserContext.CurrentEmployee = await EmployeeService.GetByShortName(userName);
    if (UserContext.CurrentEmployee == null)
    {
        NavigationManager.NavigateTo("/accessdenied");
    }
}
  1. 组件中使用
@inject UserContext UserContext

protected override void OnInitialized()
{
    if (UserContext.CurrentEmployee == null)
    {
        NavigationManager.NavigateTo("/accessdenied");
        return;
    }
    EmployeeId = UserContext.EmployeeId;
}

优势:

  • 逻辑直观,适合新手理解
  • Scoped生命周期确保每个用户会话有独立的上下文实例
方案3:Session中间件(备选)

利用ASP.NET Core Session存储员工信息,在用户首次请求时查库并存入Session,后续组件从Session读取。

注意事项:

Blazor Server基于SignalR长连接,HttpContext仅在初始请求时有效,后续SignalR消息中无法直接访问Session,因此该方案仅适合初始加载场景,若用户重新连接需重新初始化。

内容的提问来源于stack exchange,提问作者SandroRiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 17:09:04