web-dev-qa-db-fra.com

Meilleure pratique pour stocker les revendications d'autorisation ASP.NET Core lors de l'authentification d'utilisateurs sur Active Directory?

Je crée un intranet d’entreprise ASP.NET Core MVC. Je veux que mes utilisateurs s'authentifient à l'aide d'Active Directory et je veux que les autorisations des utilisateurs (revendications) soient stockées dans ApplicationDbContext .

Je suppose que je dois utiliser Microsoft.AspNetCore.Identity et Microsoft.AspNetCore.Identity.EntityFrameworkCore pour atteindre mes objectifs. Quelle est la meilleure pratique pour stocker les revendications d’autorisation ASP.NET Core lors de l’authentification auprès de Active Directory?

Le code suivant me permettra d'accéder au contexte de sécurité actuel de l'utilisateur Windows (utilisateur actuellement connecté), à partir du pipeline. D'une manière ou d'une autre, j'ai besoin de mapper l'utilisateur avec Microsoft.AspNetCore.Identity Claims?

 app.Use(async (context, next) =>
 {
      var identity = (ClaimsIdentity) context.User.Identity;
      await next.Invoke();
 });

Merci d'avance pour l'aide!

15
Will

Je ne pense pas qu'il existe une meilleure pratique autour de cela, mais vous avez beaucoup d'options. Peut-être pourriez-vous élaborer un peu sur ce que vous essayez de réaliser? Voici quelques conseils qui pourraient vous aider à résoudre certains problèmes. Juste une question ... qu'utilisez-vous pour vous authentifier contre AD sous le capot? Si vous utilisez IdentityServer ou quelque chose de similaire, je vous recommanderais de vous pencher vers l'option 4.

Option 1 - Stocker les revendications en tant que propriété ApplicationUser

Tout d'abord, les revendications sont simplement des paires clé-valeur. Cela signifie que vous pouvez créer une structure telle que:

public class UserClaim 
{
    public string UserName {get; set;}
    public string Key {get; set;}
    public string Value {get; set;}
}

Cela vous permettrait de stocker les revendications dans votre classe ApplicationUser.

Option 2 - Stockage des revendications à l'aide de UserManager

Mieux encore, vous pouvez exploiter les composants d’identité intégrés en injectant un UserManager<ApplicationUser> userManager dans la classe à laquelle vous souhaitez ajouter les revendications de l’utilisateur, puis appelez AddClaim() avec les valeurs appropriées. En raison du système d’ID dans Core, vous êtes libre de le faire dans n’importe quelle classe qui sera activée par le moteur d’exécution.

Option 3 - Stockage des revendications dans leur propre table

Une autre approche consisterait à augmenter la classe UserClaim avec une propriété UserName, puis à utiliser l'identificateur unique du principe (User.Identity.Name). Ensuite, vous pouvez le stocker dans un DbSet<UserClaim> dans votre ApplicationDbContext et récupérer les revendications par nom d'utilisateur.

Option 4 - Ne pas stocker les revendications

Si vous avez juste besoin d'accéder aux revendications et de ne pas les stocker (je ne suis pas sûr de vos intentions à partir de votre question), alors vous feriez mieux d'accéder à la propriété User si vous êtes dans un contrôleur, à condition que vous utilisiez un service d'authentification qui hydrate correctement les revendications. 

La User est décorée avec les revendications qui appartiennent à l'utilisateur qui s'est connecté à votre application et est disponible sur chaque contrôleur.

Vous pouvez sinon obtenir la ClaimsPrinciple et accéder aux revendications de l'utilisateur de cette manière. Dans ce cas (en dehors d'un contrôleur), ce que vous feriez est d'ajouter un IHttpContextAccessor accessor au constructeur de votre classe et d'accéder à la propriété HttpContextAccessor.HttpContext.User, qui est à nouveau une ClaimsPrinciple.

4
MisterJames

C'était mon approche:

Implémenter la classe MyUser

public class MyUser: IdentityUser
{
}

Utiliser ASP.Net Core DbContext dans un DBContext "sécurité" distinct (je préfère les contextes liés responsables de leurs propres fonctionnalités)

public class SecurityDbContext : IdentityDbContext<MyUser>
{
    public SecurityDbContext(DbContextOptions<SecurityDbContext> options)
        : base(options)
    { }
}

Créer un transformateur de revendications. Ceci est utilisé pour ajouter les revendications du magasin dans ClaimsIdentity (l'objet User dans votre HttpContext)

public class ClaimsTransformer : IClaimsTransformer
{
    private readonly SecurityDbContext _context;
    private readonly UserManager<MyUser> _userManager;

    public ClaimsTransformer(SecurityDbContext context, UserManager<MyUser> userManager)
    {
        _context = context;
        _userManager = userManager;
    }

    public Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
    {
        var identity = context.Principal.Identity as ClaimsIdentity;
        if (identity == null) return Task.FromResult(context.Principal);

        try
        {
            if (context.Context.Response.HasStarted)
                return Task.FromResult(context.Principal);

            var claims = AddClaims(identity);
            identity.AddClaims(claims);
        }
        catch (InvalidOperationException)
        {
        }
        catch (SqlException ex)
        {
            if (!ex.Message.Contains("Login failed for user") && !ex.Message.StartsWith("A network-related or instance-specific error"))
                throw;
        }

        return Task.FromResult(context.Principal);
    }

    private IEnumerable<Claim> AddClaims(IIdentity identity)
    {
        var claims = new List<Claim>();
        var existing = _userManager.Users.Include(u => u.Claims).SingleOrDefault(u => u.UserName == identity.Name);
        if (existing == null) return claims;

        claims.Add(new Claim(SupportedClaimTypes.ModuleName, Constants.ADGroupName));
        claims.AddRange(existing.Claims.Select(c => c.ToClaim()));

        return claims;
    }
}

Vous devrez ajouter quelques éléments dans votre classe startup.cs. Je n'ai ajouté que les éléments pertinents ici:

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentity<FreightRateUser, IdentityRole>(config =>
            {
                config.User.AllowedUserNameCharacters =
                    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@\\";
            config.User.RequireUniqueEmail = false;
            config.Cookies.ApplicationCookie.LoginPath = "/Auth/Login";
        })
        .AddEntityFrameworkStores<SecurityDbContext>();
}

N'oubliez pas la méthode Configure

public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseIdentity();
}

Enfin, vous aurez besoin de View pour ajouter l'utilisateur/les revendications appropriées aux tables d'identité. J'ai un contrôleur avec quelques actions (montrant uniquement la création ici):

    [AllowAnonymous]
    public IActionResult CreateAccess()
    {
        var vm = new AccessRequestViewModel
        {
            Username = User.Identity.Name
        };
        return View(vm);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    [AllowAnonymous]
    public async Task<IActionResult> CreateAccess(AccessRequestViewModel viewModel)
    {
        if (User == null || !User.Identity.IsAuthenticated) return View("Index");

            var newUser = new MyUser
            {
                UserName = viewModel.Username
            };
            var x = await _userManager.CreateAsync(newUser);
            if (!x.Succeeded)
                return View(ModelState);

            var myClaims = new List<Claim>();
            if (viewModel.CanManageSecurity)
                myClaims.Add(new Claim(SupportedClaimTypes.Security, "SITE,LOCAL"));
            if (viewModel.CanChangeExchangeRates)
                myClaims.Add(new Claim(SupportedClaimTypes.ExchangeRates, "AUD,USD"));
            if (viewModel.CanChangeRates)
                myClaims.Add(new Claim(SupportedClaimTypes.Updates, "LOCAL"));
            if (viewModel.CanManageMasterData)
                myClaims.Add(new Claim(SupportedClaimTypes.Admin, "SITE,LOCAL"));

            await _userManager.AddClaimsAsync(newUser, myClaims);
        }
        return View("Index");
    }

Lorsque l'utilisateur est enregistré et que vous affichez à nouveau la page authentifiée (Index), le transformateur de revendications charge les revendications dans ClaimsIdentity et toutes doivent être correctes. 

Remarque: ceci est destiné à l'utilisateur actuel de HttpContext, qui voudra créer pour d'autres utilisateurs de la création, car vous ne voulez évidemment pas que l'utilisateur actuel se donne accès.

0
Allan Nielsen