web-dev-qa-db-fra.com

Obtenir l'utilisateur actuel dans un composant Blazor

Je démarre un nouveau site avec Blazor et l'authentification Windows et je dois identifier l'utilisateur actuel qui consulte la page/le composant.

Pour une page Razor, le nom d'utilisateur actuel est accessible avec Context.User.Identity.Name, mais cela ne semble pas fonctionner dans un composant Blazor. J'ai essayé d'injecter HttpContext dans le composant mais le contexte est nul au moment de l'exécution.

En prime, je souhaiterai éventuellement l'incorporer dans Startup.cs afin que je n'ai besoin d'obtenir le nom d'utilisateur qu'une seule fois et que je puisse utiliser une classe d'utilisateurs d'entreprise (avec EF Core) pour mes applications. Des réponses adaptées à ce cas d'utilisation seraient également appréciées.

4
Wes H

J'ai maintenant pu le faire fonctionner avec une classe générale, ainsi qu'un composant.

Pour accéder à l'utilisateur HttpContext; dans ConfigureServices, dans Startup.cs, ajoutez

services.AddHttpContextAccessor();

J'ai une classe CorporateUserService pour ma classe CorporateUser. La classe de service obtient un DbContext via une injection de constructeur.

J'ai ensuite créé un nouveau CurrentCorporateUserService qui hérite du CorporateUserService. Il accepte un DbContext et un IHttpContextAccessor via une injection de constructeur

public class CurrentCorporateUserService : CorporateUserService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

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

La classe de service de base a une méthode GetUserByUsername(string username). La classe de service actuelle ajoute une méthode supplémentaire

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

La classe de service actuelle est enregistrée dans Startup.cs

services.AddScoped<CurrentCorporateUserService>();

Une fois que cela est fait, je peux utiliser le CurrentCorporateUserService dans un composant avec injection de directive.

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

ou dans n'importe quelle classe, avec injection de constructeur.

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

En faire un service à l'échelle du projet signifie que tous mes développeurs n'ont pas à se soucier de savoir comment obtenir l'utilisateur actuel, ils ont juste besoin d'injecter le service dans leur classe.

Par exemple, son utilisation dans MyDbContext rend l'utilisateur actuel disponible pour chaque événement de sauvegarde. Dans le code ci-dessous, toute classe qui hérite de la classe BaseReport verra automatiquement les métadonnées du rapport mises à jour lors de l'enregistrement de l'enregistrement.

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

MISE À JOUR - Cette réponse ne fonctionne pas. Plutôt que de le supprimer, je l'ai laissé ici comme information. Veuillez plutôt considérer les autres réponses à la question.

  1. Dans ConfigureServices, dans Startup.cs, ajoutez services.AddHttpContextAccessor();
  2. Dans votre classe de composant [Remarque: j'utilise code-behind avec les types NULL activés]
        [Inject]
        private IHttpContextAccessor HttpContextAccessor { get; set; } = default!;

        private string username = default!;
  1. Dans le code de votre composant (code derrière), dans protected override void OnInitialized()
        username = HttpContextAccessor.HttpContext.User.Identity.Name;

username peut maintenant être utilisé dans tout le composant comme n'importe quelle autre variable.

Cependant, voir mon autre réponse dans cette question pour ajouter obtenir le nom d'utilisateur actuel d'un service utilisable dans n'importe quelle classe.

1
Wes H

Ce fut un voyage douloureux pour moi à la poursuite d'une cible en mouvement. Dans mon cas, je n'avais besoin que du nom d'utilisateur pour mon composant Blazor utilisé dans une page Razor. Ma solution nécessitait les éléments suivants:

Dans Index.cshtml.cs, j'ai ajouté deux propriétés et un constructeur

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

Puis dans Index.cshtml où j'ajoute le composant que je l'ai appelé comme suit:

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

Dans mon composant, j'utilise un code derrière le fichier (FileMain.razor.cs en utilisant la classe publique FileMainBase: ComponentBase) ont le code:

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

puis comme preuve de concept, j'ai ajouté à la page FileMain.razor

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

Pour info, j'ai eu une exigence similaire et j'utilise:

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

J'avais déjà un AuthenticationsStateProvider dans mon startup.cs et je l'ai ajouté au constructeur de ma classe personnalisée.

0
DarrenB