Aller au contenu principal

Gestion des utilisateurs

Notions théoriques

Qu'est-ce qu'ASP.NET Core Identity ?

ASP.NET Core Identity est le système d'authentification et de gestion des utilisateurs intégré à ASP.NET Core. Il fournit :

  • Inscription (création de compte avec mot de passe haché automatiquement)
  • Connexion (vérification du mot de passe + création de cookie de session)
  • Déconnexion
  • Gestion des rôles (Admin, Auteur, Lecteur...)
  • Protection de routes avec [Authorize]
Comparaison avec Symfony Security et Spring Security
FrameworkSystème d'authentification
ASP.NET CoreASP.NET Core Identity
SymfonySecurity Bundle (Guard, Voters)
Spring BootSpring Security

Dans les trois frameworks, le hachage des mots de passe (BCrypt) est géré automatiquement. Vous ne stockez et ne comparez jamais les mots de passe en clair.

Installer le package Identity

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore

Configurer Identity

1. Créer ApplicationUser (votre classe utilisateur, qui hérite de IdentityUser) :

// Models/ApplicationUser.cs
using Microsoft.AspNetCore.Identity;

public class ApplicationUser : IdentityUser
{
// Propriétés supplémentaires spécifiques à MonBlog
public string NomComplet { get; set; } = string.Empty;
public DateTime DateInscription { get; set; } = DateTime.UtcNow;
}

2. Faire hériter le DbContext de IdentityDbContext :

// Data/MonBlogContext.cs
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;

public class MonBlogContext : IdentityDbContext<ApplicationUser>
{
public MonBlogContext(DbContextOptions<MonBlogContext> options) : base(options) { }

public DbSet<Article> Articles { get; set; }
// Identity ajoute automatiquement : AspNetUsers, AspNetRoles, AspNetUserRoles...
}

3. Configurer Identity dans Program.cs :

// Program.cs
builder.Services.AddDbContext<MonBlogContext>(options =>
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));

builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequiredLength = 8;
options.Password.RequireDigit = true;
options.Password.RequireUppercase = false; // Assouplissement pour les débutants
options.SignIn.RequireConfirmedAccount = false;
})
.AddEntityFrameworkStores<MonBlogContext>()
.AddDefaultTokenProviders();

// Après builder.Build() :
app.UseAuthentication(); // Avant UseAuthorization !
app.UseAuthorization();

Inscription d'un utilisateur

// Controllers/AccountController.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;

public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}

// GET /Account/Register
public IActionResult Register() => View();

// POST /Account/Register
[HttpPost, ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (!ModelState.IsValid) return View(model);

var user = new ApplicationUser
{
UserName = model.Email,
Email = model.Email,
NomComplet = model.NomComplet,
};

// Identity hache automatiquement le mot de passe (BCrypt)
var result = await _userManager.CreateAsync(user, model.Password);

if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}

// Ajouter les erreurs Identity à ModelState
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return View(model);
}
}

Connexion et déconnexion

// POST /Account/Login
[HttpPost, ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (!ModelState.IsValid) return View(model);

var result = await _signInManager.PasswordSignInAsync(
model.Email,
model.Password,
isPersistent: model.RememberMe, // "Se souvenir de moi"
lockoutOnFailure: true); // Bloquer après trop d'échecs

if (result.Succeeded)
return RedirectToAction("Index", "Home");

ModelState.AddModelError(string.Empty, "Email ou mot de passe incorrect.");
return View(model);
}

// POST /Account/Logout
[HttpPost, ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
return RedirectToAction("Index", "Home");
}

Protéger les routes avec [Authorize]

// Require une connexion pour toutes les actions du contrôleur
[Authorize]
public class ArticlesController : Controller
{
// GET /Articles/Create — accessible seulement si connecté
public IActionResult Create() => View();
}

// Ou protéger une action spécifique
public class HomeController : Controller
{
public IActionResult Index() => View(); // Public

[Authorize] // Requiert connexion
public IActionResult Profil() => View();

[Authorize(Roles = "Admin")] // Requiert rôle Admin
public IActionResult Admin() => View();
}

Dans les vues Razor, vérifier si l'utilisateur est connecté :

@using Microsoft.AspNetCore.Identity
@inject SignInManager<ApplicationUser> SignInManager

@if (SignInManager.IsSignedIn(User))
{
<a asp-controller="Account" asp-action="Logout">Déconnexion</a>
}
else
{
<a asp-controller="Account" asp-action="Login">Connexion</a>
}

Exemple pratique

ViewModel pour l'inscription et la connexion :

// ViewModels/RegisterViewModel.cs
using System.ComponentModel.DataAnnotations;

public class RegisterViewModel
{
[Required, MaxLength(100)]
[Display(Name = "Nom complet")]
public string NomComplet { get; set; } = string.Empty;

[Required, EmailAddress]
[Display(Name = "Adresse e-mail")]
public string Email { get; set; } = string.Empty;

[Required]
[StringLength(100, MinimumLength = 8, ErrorMessage = "Le mot de passe doit contenir au moins 8 caractères.")]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; } = string.Empty;

[Required]
[DataType(DataType.Password)]
[Display(Name = "Confirmer le mot de passe")]
[Compare("Password", ErrorMessage = "Les mots de passe ne correspondent pas.")]
public string ConfirmPassword { get; set; } = string.Empty;
}
// ViewModels/LoginViewModel.cs
using System.ComponentModel.DataAnnotations;

public class LoginViewModel
{
[Required, EmailAddress]
[Display(Name = "Adresse e-mail")]
public string Email { get; set; } = string.Empty;

[Required, DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; } = string.Empty;

[Display(Name = "Se souvenir de moi")]
public bool RememberMe { get; set; }
}
astuce

ASP.NET Core Identity génère automatiquement les tables AspNetUsers, AspNetRoles, AspNetUserRoles via les migrations EF Core. Après avoir configuré Identity, générez une migration : dotnet ef migrations add AjoutIdentity.

Test de mémorisation/compréhension


Quelle classe de base ASP.NET Core Identity représente un utilisateur ?


Quel service Identity crée et gère les comptes utilisateurs (création, suppression, rôles...) ?


Quel service Identity gère la connexion et la déconnexion (cookies de session) ?


Quel attribut C# protège une action MVC et exige que l'utilisateur soit connecté ?


De quelle classe le DbContext doit-il hériter pour intégrer ASP.NET Core Identity ?


ASP.NET Core Identity hache-t-il automatiquement les mots de passe ?


TP pour réfléchir et résoudre des problèmes

Vous allez configurer ASP.NET Core Identity dans MonBlog et créer la page d'inscription.

Étape 1 — Configurer Identity dans Program.cs


Bonne pratique - Assouplir les règles de mot de passe en formation

En développement avec des étudiants, assoupliez les règles de mot de passe (RequireUppercase = false, RequireNonAlphanumeric = false) pour faciliter les tests. En production, appliquez des règles strictes pour la sécurité des vrais utilisateurs.

Étape 2 — Créer le formulaire d'inscription


Bonne pratique - Connecter après inscription

Connecter l'utilisateur automatiquement après l'inscription améliore l'expérience utilisateur (pas besoin de se connecter manuellement). L'option isPersistent: false crée un cookie de session (expiré à la fermeture du navigateur). Utilisez isPersistent: true pour "Se souvenir de moi".

Étape 3 — Générer la migration Identity


Bonne pratique - Une migration par fonctionnalité

Créez une migration distincte pour l'ajout d'Identity (plutôt que de la mélanger à d'autres changements). Cela facilite la lecture de l'historique Git et permet de faire un rollback ciblé si nécessaire.

📌 Une solution