在 Blazor 组件中获取当前用户

Wes*_*s H 25 asp.net-core blazor blazor-server-side .net-core-3.0 ef-core-3.0

我正在使用 Blazor 和 Windows 身份验证启动一个新站点,并且需要识别查看页面/组件的当前用户。

对于 Razor 页面,可以使用 访问当前用户名Context.User.Identity.Name,但这在 Blazor 组件中似乎不起作用。我尝试将 HttpContext 注入组件,但 Context 在运行时为空。

作为奖励,我最终希望将其合并到 Startup.cs 中,因此我只需要获取一次用户名,就可以为我的应用程序利用企业用户类(使用 EF Core)。也将不胜感激针对该用例量身定制的答案。

Ric*_*res 38

在组件中获取用户有三种可能性(页面就是组件):

  1. 注入IHttpContextAccessor并从中访问HttpContext然后User;需要注册IHttpContextAccessorStartup.ConfigureServices,正常使用AddHttpContextAccessor
  2. 注入AuthenticationStateProvider属性,调用GetAuthenticationStateAsync并获得User从中
  3. 将您的组件包装在一个<CascadingAuthenticationState>组件中,声明一个Task<AuthenticationState>属性并调用它以获取User(类似于#2)

在此处查看更多信息:https : //docs.microsoft.com/en-us/aspnet/core/security/blazor

  • “注入 IHttpContextAccessor 并从中访问 HttpContext,然后访问 User”。这不是真的。WebSocket 连接与 HTTP 请求有什么关系 (7认同)
  • 选项 1 在开发过程中对我有用,但在 IIS 中发布后就不起作用了。?? 所以我结束了使用选项2 (6认同)
  • 1、IHttpContextAccessor -&gt; 不,不不,从来没有!:) 无需进行“专业”解释,实际上它返回了在用户之间共享的服务器的 HttpContext!实际上,它导致了我现在遇到的一些问题:a,用户信息,角色混淆,b,通过更改主题将应用于所有其他用户!所以我今天学到的是:永远不要使用 IHttpContextAccessor,永远不要做任何单例/静态的事情!非常仔细地检查注入的服务!有些服务注册为 services.AddXYZ 会自动注册为 Singleton!选项2和3都很好,是很好的建议。 (6认同)
  • @IstvánPiroska - 这似乎不对。根据文档,HttpContext“封装有关单个 HTTP 请求的所有 HTTP 特定信息”。如果身份已到位,这包括该请求的用户。如果我错了,我想知道。您能给出一个“专业”的解释吗?我过去一直使用 HttpContext 来检索当前的主要声明。 (6认同)
  • 选项 2 应该是首选。 (2认同)
  • 选项 2 对我有用,并且很容易用于在页面中显示用户名。@authStateProvider.GetAuthenticationStateAsync().Result.User.Identity.Name (2认同)

fin*_*s10 19

对于blazor wasmnet 5.0及以上。这是我的做法,

  1. <App>将您的组件包裹在里面<CascadingAuthenticationState>,如下所示,
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            ...
        </Found>
        <NotFound>
            ...
        </NotFound>
    </Router>
</CascadingAuthenticationState>
Run Code Online (Sandbox Code Playgroud)
  1. 然后添加到Task<AuthenticationState> CascadingParameter任何组件内,如下所示,
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            ...
        </Found>
        <NotFound>
            ...
        </NotFound>
    </Router>
</CascadingAuthenticationState>
Run Code Online (Sandbox Code Playgroud)
  1. 现在您可以访问登录的用户IdentityClaims内部组件,如下所示,
public class AppRootBase : ComponentBase
{
    [CascadingParameter] private Task<AuthenticationState> authenticationStateTask { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这是来自Microsoft 文档的参考。


Wes*_*s H 10

我现在已经能够让它与一个通用类以及一个组件一起工作。

访问HttpContext用户;在ConfigureServicesStartup.cs加上

services.AddHttpContextAccessor();
Run Code Online (Sandbox Code Playgroud)

我有CorporateUserService我的课CorporateUser类。服务类DbContext通过构造函数注入。

然后我创建了一个CurrentCorporateUserService继承自CorporateUserService. 它接受一个DbContext和一个IHttpContextAccessor通过构造函数注入

public class CurrentCorporateUserService : CorporateUserService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CurrentCorporateUserService(IHttpContextAccessor httpContextAccessor,
        MyDbContext context) : base(context)
    {
        _httpContextAccessor = httpContextAccessor;
    }
    ...
Run Code Online (Sandbox Code Playgroud)

基本服务类有一个方法GetUserByUsername(string username)。Current 服务类添加了一个额外的方法

public CorporateUser GetCurrentUser()
{
    return base.GetUserByUsername(_httpContextAccessor.HttpContext.User.Identity.Name.Substring(8));
}
Run Code Online (Sandbox Code Playgroud)

当前服务类注册在 Startup.cs

services.AddScoped<CurrentCorporateUserService>();
Run Code Online (Sandbox Code Playgroud)

完成后,我可以CurrentCorporateUserService在带有指令注入的组件中使用 。

[Inject]
private CurrentCorporateUserService CurrentCorporateUserService { get; set; } = 
default!;
Run Code Online (Sandbox Code Playgroud)

或在任何类中,使用构造函数注入。

public MyDbContext(DbContextOptions<MyDbContext> options,
    CurrentCorporateUserService CurrentCorporateUserService)
    : base(options) 
{
    _currentUser = CurrentCorporateUserService.GetCurrentUser();
}
Run Code Online (Sandbox Code Playgroud)

使其成为项目范围的服务意味着我所有的开发人员都不必关心如何获取当前用户,他们只需要将服务注入到他们的类中。

例如,使用它MyDbContext使当前用户可用于每个保存事件。在下面的代码中,任何继承BaseReport该类的类都将在保存记录时自动更新报表元数据。

public override Int32 SaveChanges()
{         
    var entries = ChangeTracker.Entries().Where(e => e.Entity is BaseReport
    && (e.State == EntityState.Added || e.State == EntityState.Modified));

    foreach (var entityEntry in entries)
    {
        ((BaseReport)entityEntry.Entity).ModifiedDate = DateTime.Now;
        ((BaseReport)entityEntry.Entity).ModifiedByUser = _currentUser.Username;
    }

    return base.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)

  • 提问者提供的这个答案是错误的。在提供任何答案之前,我已经对有关它的问题发表了评论。但这里的人们,包括提问者,选择忽略它。请阅读上面 Balagurunathan Marimuthu 的评论...... (3认同)
  • 正如上面的答案所述,不要这样做。有关详细信息,请参阅 https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-6.0。 (2认同)

小智 10

我有类似的要求并一直在使用:

var authstate = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authstate.User;
var name = user.Identity.Name;
Run Code Online (Sandbox Code Playgroud)

我已经AuthenticationStateProvider在我的startup.cs中有一个并将其添加到我的自定义类的构造函数中。


Ogg*_*las 7

Shared\LoginDisplay.razor如果您创建一个新项目并选择使用 Windows 身份验证的 Blazor,您将获得一个包含以下内容的文件:

<AuthorizeView>
    Hello, @context.User.Identity.Name!
</AuthorizeView>
Run Code Online (Sandbox Code Playgroud)

使用我们可以在任何页面上无需任何修改<AuthorizeView>即可访问。@context.User.Identity.Name

在此输入图像描述


小智 7

在 App.razor 中,确保该元素封装在 CascadingAuthenticationState 元素内。这是如果您创建具有身份验证支持的 Blazor 项目时默认生成的内容。

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
Run Code Online (Sandbox Code Playgroud)

在您的组件中,您可以使用 AuthenticationStateProvider 来访问当前用户,如以下示例所示:

@page "/"

@layout MainLayout

@inject AuthenticationStateProvider AuthenticationStateProvider
@inject SignInManager<IdentityUser> SignInManager

@code
{
    override protected async Task OnInitializedAsync()
    {
        var authenticationState = await AuthenticationStateProvider.GetAuthenticationStateAsync();

        if (SignInManager.IsSignedIn(authenticationState.User))
        {
            //Do something...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 6

For me the solution mentioned in the first answer 2. option worked perfect: I am using Blazor server side on .Net Core 5.0 . I injected

@inject AuthenticationStateProvider GetAuthenticationStateAsync
Run Code Online (Sandbox Code Playgroud)

in my Blazor page and added the following in the code section:

protected async override Task OnInitializedAsync()
    {
        var authstate = await GetAuthenticationStateAsync.GetAuthenticationStateAsync();
        var user = authstate.User;
        var name = user.Identity.Name;
    }
Run Code Online (Sandbox Code Playgroud)

In my startup.cs, I have the following lines:

services.AddScoped<ApiAuthenticationStateProvider>();
services.AddScoped<AuthenticationStateProvider>(p =>
                p.GetRequiredService<ApiAuthenticationStateProvider>());
Run Code Online (Sandbox Code Playgroud)

  • 我认为在 .NET 5.0 中不需要添加“Startup”。 (7认同)
  • 这对我来说不适用于 Blazor WASM + .Net 5.0。当我登录时,“authState.User.Identity.Name”始终为空。但是当我刷新时,“.Name”就可以了。 (2认同)
  • 这对我使用 .NET 6.0 来说效果很好。 (2认同)