Get Current User in a Blazor component

余生颓废 提交于 2020-08-02 19:01:29

问题


I'm starting a new site with Blazor and Windows Authentication and need to identify the current user viewing the page/component.

For a Razor Page, the current user name can be accessed with Context.User.Identity.Name, but that doesn't seem to work in a Blazor component. I've tried injecting HttpContext into the component but the Context is null at runtime.

As a bonus, I will eventually want to incorporate this into Startup.cs so I only need to get the username once and can leverage a corporate user class (with EF Core) for my applications. Answers tailored to that use case would also be appreciated.


回答1:


There are three possibilities for getting the user in a component (a page is a component):

  1. Inject IHttpContextAccessor and from it access HttpContext and then User
  2. Inject a AuthenticationStateProvider property, call GetAuthenticationStateAsync and get User from it
  3. Wrap your component in a <CascadingAuthenticationState> component, declare a Task<AuthenticationState> property and call it to get the User (similar to #2)

See more here: https://docs.microsoft.com/en-us/aspnet/core/security/blazor.




回答2:


I've now been able to get it to work with a general class, as well as a component.

To get access to the HttpContext User; in ConfigureServices, in Startup.cs add

services.AddHttpContextAccessor();

I have a CorporateUserService class for my CorporateUser class. The service class gets a DbContext through constructor injection.

I then created a new CurrentCorporateUserService that inherits from the CorporateUserService. It accepts a DbContext and an IHttpContextAccessor through constructor injection

public class CurrentCorporateUserService : CorporateUserService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CurrentCorporateUserService(IHttpContextAccessor httpContextAccessor, MyDbContext context) : base(context)
    {
        _httpContextAccessor = httpContextAccessor;
    }
    ...

The base service class has a method GetUserByUsername(string username). The Current service class adds an additional method

public CorporateUser GetCurrentUser()
{
    return base.GetUserByUsername(_httpContextAccessor.HttpContext.User.Identity.Name.Substring(8));
}

The Current service class is registered in Startup.cs

services.AddScoped<CurrentCorporateUserService>();

Once that is done, I can use the CurrentCorporateUserService in a component with directive injection.

    [Inject]
    private CurrentCorporateUserService CurrentCorporateUserService { get; set; } = 
    default!;

or in any class, with constructor injection.

    public MyDbContext( DbContextOptions<MyDbContext> options
                  , CurrentCorporateUserService CurrentCorporateUserService )
                  : base(options) 
    {
        _currentUser = CurrentCorporateUserService.GetCurrentUser();
    }

Making it a project wide service means all my developers do not have to concern themselves with how to get the Current User, they just need to inject the service into their class.

For example, using it in MyDbContext makes the current user available to every save event. In the code below, any class that inherits the BaseReport class will automatically have the report metadata updated when the record is saved.

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();
}



回答3:


UPDATE - This answer does not work. Rather than deleting it, I've let it here as information. Please consider the other answers for the question instead.

  1. In ConfigureServices, in Startup.cs, add services.AddHttpContextAccessor();
  2. In your component class [Note: I use code-behind with null types enabled]
        [Inject]
        private IHttpContextAccessor HttpContextAccessor { get; set; } = default!;

        private string username = default!;
  1. In your component code (code behind), in protected override void OnInitialized()
        username = HttpContextAccessor.HttpContext.User.Identity.Name;

username can now be used throughout the component just like any other variable.

However, see my other answer in this question to add get the current user name from a service usable in any class.




回答4:


This was a painful journey for me chasing a moving target. In my case I only needed the user name for my Blazor component used in a Razor page. My solution required the following:

In the Index.cshtml.cs I added two properties and constructor

    public IHttpContextAccessor HttpContextAccessor { get; set; }
    public string UserName { get; set; }

    public TestModel(IHttpContextAccessor httpContextAccessor)
    {
        HttpContextAccessor = httpContextAccessor;
        if (HttpContextAccessor != null) UserName = HttpContextAccessor.HttpContext.User.Identity.Name;
    }

Then in the Index.cshtml where I add the component I called it as follows:

<component type="typeof(MyApp.Components.FileMain)" param-UserName="Model.UserName" render-mode="ServerPrerendered" />

In my component I use a code behind file (FileMain.razor.cs using public class FileMainBase : ComponentBase) have the code:

    [Parameter]
    public string UserName { get; set; } = default!;

and then as a proof of concept I added to the FileMain.razor page

    <div class="form-group-sm">
    <label class="control-label">User: </label>
    @if (UserName != null)
    {
        <span>@UserName</span>
    }
</div>



回答5:


FYI I've had a similar requirement and have been using:

var authstate = await AuthenticationsStateProvider.GetAuthenticationStateAsync();
var user = authstate.User;
var name = user.Identity.Name;

I already had an AuthenticationsStateProvider in my startup.cs and added it to the constructor of my custom class.



来源:https://stackoverflow.com/questions/60264657/get-current-user-in-a-blazor-component

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!