ASP.net – Partager la session entre plusieurs sous-domaines avec http ou https
Dans certains cas il peut être intéressant de pouvoir partager une session entre plusieurs sous-domaines. Cela nous permet par exemple d’avoir une session unique pour les urls fr.monsite.com et en.monsite.com ou http://www.monsite.com et https://secure.monsite.com.
Afin de bien comprendre notre problème, intéressons nous au fonctionnement des sessions.
Tout d’abord une session est partagée entre les pages d’un site via un cookie. Par défaut, ce cookie a pour nom ASP.NET_SessionId et contient une suite de 24 caractères alpha numériques aléatoires.
Lorsque l’on fait une requête vers ASP.net, un HttpContext est instancié. Ce contexte passe au travers de différents HttpModule dont le SessionStateModule, c’est ce module qui va se charger d’orchestrer les différentes étapes de la création / sauvegarde des variables de session. Dans un premier temps, ce module va récupérer un ID de session via le SessionIDManager, ce manager va regarder dans la requête s’il y a ou non le cookie de session contenant l’ID. S’il n’est pas présent, il va alors générer et retourner un ID unique. Le module va ensuite interroger le SessionStateStoreProvider afin de récupérer l’état de la session pour l’assigner au contexte de la requête.
Le traitement de la page s’exécute alors. Viens ensuite l’événement ReleaseRequesState qui va sauvegarder la session dans le SessionStateStoreProvider puis persister le SessionID dans la réponse renvoyé au client.
Deux classes sont responsables du fonctionnement des sessions, le SessionIDManager et le SessionStateStoreProvider. Le SessionStateStoreProvider permet de stocker les variables de sessions alors que SessionIDManager permet de sauvegarder côté client l’identifiant de session. ASP.net nous permet de personnaliser ces 2 classes via des interfaces.
Dans notre cas, nous avons besoin de créer notre propre SessionIDManager, en effet c’est lui qui est responsable de la création du cookie. Afin de simplifier l’implémentation, nous allons encapsuler le SessionIDManager natif, lors de la méthode SaveSessionID nous allons modifier la propriété Domain du cookie de session.
public class CustomSessionIDManager : ISessionIDManager
{
private ISessionIDManager _innerSessionIDManager;
public CustomSessionIDManager()
{
this._innerSessionIDManager = new SessionIDManager();
}
public void SaveSessionID(HttpContext context, string id,
out bool redirected, out bool cookieAdded)
{
this._innerSessionIDManager.SaveSessionID(context, id,
out redirected, out cookieAdded);
// POC : not the best solution
if (cookieAdded)
{
context.Response.Cookies["ASP.NET_SessionId"].Domain = "local.test";
}
}
// ...
}
L’enregistrement de notre SessionIDManager se fait en renseignant la propriété sessionIDManagetType de la section sessionState du fichier de config.
<configuration>
<system.web>
<sessionState sessionIDManagerType="CustomSessionIDManager, App_Code" />
</system.web>
</configuration>
Cette solution n’est pas optimum, en effet le nom du cookie est écrit en dur ainsi que le domaine. Le nom du cookie est spécifié via la propriété cookieName de la section sessionState du web.config, il nous est alors facilement récupérable. Pour le nom du domaine, nous avons 2 solutions, soit l’on réussit à le faire transiter du fichier de config vers notre SessionIDManager, soit on le calcul via le domaine courant. Malheureusement, on ne peut pas ajouter de paramètres au SessionIDManager via la section sessionState. Nous allons utiliser les AppSettings et le calcul si la clé n’est pas définit.
public class CustomSessionIDManager : ISessionIDManager
{
private String _cookieName;
private ISessionIDManager _innerSessionIDManager;
public CustomSessionIDManager()
{
this._innerSessionIDManager = new SessionIDManager();
}
public void Initialize()
{
this._innerSessionIDManager.Initialize();
SessionStateSection sessionStateSection =
(SessionStateSection)ConfigurationManager.GetSection("system.web/sessionState");
this._cookieName = sessionStateSection.CookieName;
}
public void SaveSessionID(HttpContext context, string id,
out bool redirected, out bool cookieAdded)
{
this._innerSessionIDManager.SaveSessionID(context, id,
out redirected, out cookieAdded);
if (cookieAdded)
{
String domainName =
ConfigurationManager.AppSettings["CustomSessionIDManager.CookieDomain"];
if (String.IsNullOrEmpty(domainName))
{
domainName = context.Request.Url.Host;
String[] domainParts = domainName.Split('.');
if (domainParts.Length > 1)
domainName = String.Join(".", domainParts
.Skip(domainParts.Length - 2)
.ToArray());
}
context.Response.Cookies[_cookieName].Domain = domainName;
}
}
// ...
}
Ainsi, nous pouvons accéder à la session entre plusieurs sous-domaines.
Bien sur, il faut que le SessionStateStoreProvider utilisé soit le même entre les différents sites. Si vous avez qu’un seul site définit pour les 2 urls au niveau de IIS, il n’est pas nécessaire de modifier le sessionStateStoreProvider. Par contre, si vous avez 2 sites différents il vous faut utiliser un serveur de session ou un serveur SQL afin de transiter les sessions entre vos 2 sites web. Pour plus d’informations sur la configuration d’un serveur de session, vous pouvez lire l’article ASP.NET Session State
Pour plus de détails sur le fonctionnement interne des sessions, je vous invite à consulter l’article Fast, Scalable, and Secure Session State Management for Your Web Applications de Michael Volodarsky.