Démarrage▲
Créez un projet ASP.Net MVC 5 sans authentification :
Compilez, exécutez. Tout est ok :
Commençons par sécuriser l'accès.
Sécurisation▲
La mise en place de l'authentification sur l'ensemble du site se fait tout simplement en ajoutant AuthorizeAttribute dans les filtres globaux. La classe filterConfig se trouve dans le répertoire App_Start :
filters.
Add
(
new
AuthorizeAttribute
(
));
AuthorizeAttribute peut se placer aussi directement sur un contrôleur ou une action, mais dans notre contexte, l'ajout en tant que filtre global est la solution idéale.
Notez que ce filtre global ne s'applique qu'aux actions MVC, et non pas aux éventuels WebForms, contenus statiques et autres Handlers ASP.Net que pourrait contenir votre application.
Compilez, exécutez. Vous obtenez une erreur HTTP 401 qui indique que l'accès à cette page est restreint aux utilisateurs authentifiés :
Créons maintenant la page de login.
Page de login▲
Ajoutez une classe LoginViewModel dans le répertoire Models :
using
System.
ComponentModel.
DataAnnotations;
namespace
OwinFormsAuthenticationTutorial.
Models
{
public
class
LoginViewModel
{
[Required]
[Display(Name =
"Identifiant"
)]
public
string
Login {
get
;
set
;
}
[Required]
[DataType(DataType.Password)]
[Display(Name =
"Mot de passe"
)]
public
string
Password {
get
;
set
;
}
}
}
Ajoutez un contrôleur AuthenticationController dans le répertoire Controllers.
Ajoutez une action Login avec l'attribut AllowAnonymous pour qu'elle puisse être exécutée sans que l'utilisateur soit authentifié :
using
System.
Web.
Mvc;
namespace
OwinFormsAuthenticationTutorial.
Controllers
{
public
class
AuthenticationController :
Controller
{
[AllowAnonymous]
public
ActionResult Login
(
)
{
return
View
(
);
}
}
}
Créez une vue Login.cshtml à partir du modèle LoginViewModel dans le répertoire Views/Authentication :
Compilez et exécutez. Sur la page d'accueil, vous obtenez toujours une erreur 401, mais si vous affichez l'URL /Authentication/Login, vous verrez la page de login s'afficher :
Comment faire maintenant pour être redirigé vers la page de login lorsque l'utilisateur n'est pas authentifié ?
Mode d'authentification et redirection▲
C'est à cette étape qu'on indique à l'application le mode d'authentification.
Installez les packages NuGet suivants :
Install-Package Microsoft.AspNet.Identity.Core
Install-Package Microsoft.Owin.Security.Cookies
Install-Package Microsoft.Owin.Host.SystemWeb
Créez une classe AuthConfig dans le répertoire App_Start :
using
Microsoft.
AspNet.
Identity;
using
Microsoft.
Owin;
using
Microsoft.
Owin.
Security.
Cookies;
using
Owin;
[assembly: OwinStartup(
typeof
(OwinFormsAuthenticationTutorial.AuthConfig))]
namespace
OwinFormsAuthenticationTutorial
{
public
class
AuthConfig
{
public
void
Configuration
(
IAppBuilder app)
{
app.
UseCookieAuthentication
(
new
CookieAuthenticationOptions
{
AuthenticationType =
DefaultAuthenticationTypes.
ApplicationCookie,
LoginPath =
new
PathString
(
"/Authentication/Login"
)
}
);
}
}
}
Dans les modèles de projets ASP.Net MVC avec authentification, cette configuration se trouve dans la classe partielle Startup (Startup.cs et App_Start/Startup.Auth.cs).
Compilez, exécutez. Vous êtes maintenant redirigés vers la page de login.
Notez le paramètre ReturnUrl dans la barre d'adresse qui sera utilisé pour rediriger l'utilisateur vers cette page une fois l'authentification réussie :
Il nous faut maintenant valider le formulaire de login afin d'authentifier l'utilisateur.
Authentification▲
Ajoutez les méthodes suivantes dans le contrôleur AuthenticationController :
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public
ActionResult Login
(
LoginViewModel model,
string
returnUrl)
{
ViewBag.
ReturnUrl =
returnUrl;
if
(!
ModelState.
IsValid)
{
return
View
(
model);
}
if
(!
ValidateUser
(
model.
Login,
model.
Password))
{
ModelState.
AddModelError
(
string
.
Empty,
"Le nom d'utilisateur ou le mot de passe est incorrect."
);
return
View
(
model);
}
// L'authentification est réussie,
// injecter l'identifiant utilisateur dans le cookie d'authentification :
var
loginClaim =
new
Claim
(
ClaimTypes.
NameIdentifier,
model.
Login);
var
claimsIdentity =
new
ClaimsIdentity
(
new
[]
{
loginClaim },
DefaultAuthenticationTypes.
ApplicationCookie);
var
ctx =
Request.
GetOwinContext
(
);
var
authenticationManager =
ctx.
Authentication;
authenticationManager.
SignIn
(
claimsIdentity);
// Rediriger vers l'URL d'origine :
if
(
Url.
IsLocalUrl
(
ViewBag.
ReturnUrl))
return
Redirect
(
ViewBag.
ReturnUrl);
// Par défaut, rediriger vers la page d'accueil :
return
RedirectToAction
(
"Index"
,
"Home"
);
}
private
bool
ValidateUser
(
string
login,
string
password)
{
// TODO : insérer ici la validation des identifiant et mot de passe de l'utilisateur...
// Pour ce tutoriel, j'utilise une validation extrêmement sécurisée...
return
login ==
password;
}
Il vous faudra ajouter les using pour Microsoft.AspNet.Identity, OwinFormsAuthenticationTutorial.Models et System.Security.Claims.
Compilez et exécutez. Saisissez les login / mot de passe : test / test, validez et boum : vous voilà connecté !
Pour visualiser le cookie sous Chrome, ouvrez les outils de développement (touche F12) et allez dans Resources > Cookies > localhost.
Là se trouve le cookie .AspNet.ApplicationCookie :
Enfin, pour boucler la boucle, rajoutons un lien de déconnexion.
Déconnexion▲
Ajoutez la méthode suivante dans le contrôleur AuthenticationController :
63.
64.
65.
66.
67.
68.
69.
70.
71.
[HttpGet]
public
ActionResult Logout
(
)
{
var
ctx =
Request.
GetOwinContext
(
);
var
authenticationManager =
ctx.
Authentication;
authenticationManager.
SignOut
(
);
// Rediriger vers la page d'accueil :
return
RedirectToAction
(
"Index"
,
"Home"
);
}
Ajoutez le lien suivant dans le menu principal (Views/Shared/_Layout.cshtml) à la suite des entrées « Accueil », « A propos de » et « Contact » :
27.
28.
29.
@if
(
Request.
IsAuthenticated)
{
<
li>
@Html.
ActionLink
(
"Se déconnecter"
,
"Logout"
,
"Authentication"
)</
li>
}
Compilez et exécutez. Cliquez sur « Se déconnecter ». Et voilà, vous retombez sur la page de login :
Ok, tout ça c'est bien joli, mais comment faire pour récupérer l'utilisateur connecté au cours de sa session ?
Identification▲
Exemple classique : afficher une salutation personnalisée avec l'identifiant de l'utilisateur.
Il suffit de récupérer les informations des revendications qui sont automatiquement alimentées par le framework dans la propriété User.Identity.
Insérez en début du fichier Views/Shared/_Layout.chstml :
@using
System.
Security.
Claims
@{
var
homeLinkText =
"Nom de l'application"
;
if
(
Request.
IsAuthenticated)
{
var
claimIdentity =
User.
Identity as
ClaimsIdentity;
if
(
claimIdentity !=
null
)
{
homeLinkText =
"Bonjour, "
+
claimIdentity.
FindFirst
(
ClaimTypes.
NameIdentifier).
Value;
}
}
}
Affichez cette variable dans le libellé du lien d'accueil :
@Html.
ActionLink
(
homeLinkText,
"Index"
,
"Home"
,
new
{
area =
""
},
new
{
@class
=
"navbar-brand"
}
)
Compilez et exécutez. Connectez-vous. Voilà votre salutation personnalisée :
Et pour stocker des informations supplémentaires dans le cookie d'authentification ?
Il suffit de rajouter des revendications (« Claims »).
Informations personnalisées▲
Autre exemple classique : ajouter les rôles de l'utilisateur dans ses revendications.
Pour cela, modifiez la méthode Login du contrôleur AuthenticationController, telle que :
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
// L'authentification est réussie,
// injecter les informations utilisateur dans le cookie d'authentification :
var
userClaims =
new
List<
Claim>(
);
// Identifiant utilisateur :
userClaims.
Add
(
new
Claim
(
ClaimTypes.
NameIdentifier,
model.
Login));
// Rôles utilisateur :
userClaims.
AddRange
(
LoadRoles
(
model.
Login));
var
claimsIdentity =
new
ClaimsIdentity
(
userClaims,
DefaultAuthenticationTypes.
ApplicationCookie);
var
ctx =
Request.
GetOwinContext
(
);
var
authenticationManager =
ctx.
Authentication;
authenticationManager.
SignIn
(
claimsIdentity);
63.
64.
65.
66.
67.
68.
69.
private
IEnumerable<
Claim>
LoadRoles
(
string
login)
{
// TODO : Charger ici les rôles de l'utilisateur...
// Pour ce tutoriel, je considère que l'utilisateur a les rôles "Contributeur" et "Modérateur" :
yield
return
new
Claim
(
ClaimTypes.
Role,
"Contributeur"
);
yield
return
new
Claim
(
ClaimTypes.
Role,
"Modérateur"
);
}
Pour les récupérer, c'est aussi dans la propriété User.Identity.
Modifiez la vue Views/Home/About.cshtml, telle que :
@using
System.
Security.
Claims
@{
ViewBag.
Title =
"About"
;
var
userRoles =
new
List<
Claim>(
);
if
(
Request.
IsAuthenticated)
{
var
claimIdentity =
User.
Identity as
ClaimsIdentity;
if
(
claimIdentity !=
null
)
{
userRoles =
claimIdentity.
FindAll
(
ClaimTypes.
Role).
ToList
(
);
}
}
}
<
h2>
@ViewBag.
Title.</
h2>
<
h3>
@ViewBag.
Message</
h3>
<
p>
Vous avez actuellement les rôles suivants :</
p>
<
ul>
@foreach
(
var
role in
userRoles)
{
<
li>
@role.
Value</
li>
}
</
ul>
Compilez, exécutez et connectez-vous sur la page /Home/About : vous avez la liste des rôles de l'utilisateur :
Conclusion▲
Voilà en quelques étapes simples la mise en place de l'authentification par formulaire à partir d'une application ASP.Net MVC 5 vide.
Le code fourni dans cet article va à l'essentiel. Le code ajouté à chaque étape est le plus court possible pour mettre en avant ce qui est important.
Dans votre application, tâchez de mieux isoler les responsabilités.
Et notamment : la récupération des revendications. Elle ne devrait pas se trouver directement dans les vues, car ces informations sont largement utilisées et vous devriez répéter le code à chaque fois. Une bonne solution est de créer des méthodes d'extension de l'interface IIdentity (ou IPrincipal) que vous pourrez appeler depuis User.Identity (ou respectivement User).
Pour stocker d'autres informations personnalisées, vous pouvez utiliser les revendications prédéfinies dans le framework : ClaimTypes ; ou vos propres revendications (ce ne sont ni plus ni moins que des clés).
Pour des scénarios plus complexes de gestion d'autorisations, regardez du côté de la classe ClaimsAuthorizationManager.
Le code source de la solution est disponible sur mon compte GitHub : OwinFormsAuthenticationTutorial.
Chaque étape de ce tutoriel est isolée dans un commit Git dont le commentaire porte le nom du chapitre, de sorte que vous pouvez voir toutes les modifications apportées à chaque étape. Vous pouvez également télécharger l'ensemble des sources pour avoir un projet vide avec l'authentification prêt à utiliser.
Merci à milkoseck pour sa relecture.