En la anterior entrega hablamos sobre como Claims Identity / Principal pasaban a ser elementos fundamentales de .NET 4.5. Junto con esta entrada también vimos como los mecanismos de autorización legacy seguían funcionando, aunque ahora interpretando claims. Pues bien, a lo largo de esta entrada trataremos de hablar de dos elementos fundamentales en nuestros RP, ClaimsAuthenticationManager y ClaimsAuthorizationManager.
ClaimsAuthenticationManager
Tal y como se puede leer en la documentación de esta clase, la idea de esta pieza es la de poder dar soporte a la transformación, modificación y agregación de las claims obtenidas en el proceso de autenticación de un usuario. Un ejemplo podría ser en un sitio web con un carrito en el que querramos agregar a cualquier usuario autenticado alguna claim procedente de nuestro sistema y no del repositorio/pieza de autenticación. Su uso, como puede verse a continuación es tremendamente sencillo.
1 |
<span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> PassThrougClaimsAuthenticationManager |
1 |
:ClaimsAuthenticationManager |
1 |
{ |
1 |
<span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> ClaimsPrincipal Authenticate(<span style="color: #0000ff">string</span> resourceName, ClaimsPrincipal incomingPrincipal) |
1 |
{ |
1 |
<span style="color: #0000ff">if</span> (incomingPrincipal != <span style="color: #0000ff">null</span> && incomingPrincipal.Identity.IsAuthenticated) |
1 |
{ |
1 |
var identity = (ClaimsIdentity)incomingPrincipal.Identity; |
1 |
  |
1 |
<span style="color: #008000">//TODO: Add, remove or change identity.Claims property</span> |
1 |
} |
1 |
  |
1 |
<span style="color: #0000ff">return</span> incomingPrincipal; |
1 |
} |
1 |
} |
Si observa, lo único que hemos hecho es sobreescribir el método Authenticate, para, si el usuario está autenticado trabajar sobre la colección de Claims de la identidad obtenida por el método seleccionado. Para establecer esta implementación como el ClaimsAuthenticationManager por defecto solamente tendremos que agregar la siguiente sección de configuración dentro de nuestro programa.
1 |
<configSections> |
1 |
<section name=<span style="color: #006080">"system.identityModel"</span> type=<span style="color: #006080">"System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"</span>/> |
1 |
</configSections> |
1 |
<system.identityModel > |
1 |
<identityConfiguration name=<span style="color: #006080">"MyIdentity"</span>> |
1 |
<claimsAuthenticationManager type=<span style="color: #006080">"ClaimsAuthentication.PassThrougClaimsAuthenticationManager,ClaimsAuthentication"</span>/> |
1 |
</identityConfiguration> |
1 |
</system.identityModel> |
Una vez establecida la configuración, tendremos que indicar que se utilize el sistema de transformación de claims, para ello, dependiendo del tipo de aplicación lo haremos de forma diferente. Por ejemplo, para una aplicación de escritorio solamente tendríamos que cambiar el sitio en el que hagamos la asignación de nuestro principal al hilo de ejecución, incorporando el siguiente código:
1 |
Thread.CurrentPrincipal = FederatedAuthentication.FederationConfiguration |
1 |
.IdentityConfiguration |
1 |
.ClaimsAuthenticationManager |
1 |
.Authenticate(<span style="color: #006080">"app"</span>, principal); |
En el caso de la web, es un poco diferente, sobre todo, porque generalmente no intervenimos en este proceso de asignación del principal al HttpContext. Para resolver esto, podemos crear un sencillo módulo Http que nos haga este trabajo, un código de ejemplo podría ser el siguiente:
1 |
<span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> ClaimsTransformationHttpModule : IHttpModule |
1 |
{ |
1 |
<span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> Init(HttpApplication context) |
1 |
{ |
1 |
context.PostAuthenticateRequest += Context_PostAuthenticateRequest; |
1 |
} |
1 |
  |
1 |
<span style="color: #0000ff">void</span> Context_PostAuthenticateRequest(<span style="color: #0000ff">object</span> sender, EventArgs e) |
1 |
{ |
1 |
var context = ((HttpApplication)sender).Context; |
1 |
  |
1 |
<span style="color: #008000">// no need to call transformation if session already exists</span> |
1 |
<span style="color: #0000ff">if</span> (FederatedAuthentication.SessionAuthenticationModule != <span style="color: #0000ff">null</span> |
1 |
&& |
1 |
FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(context.Request.Cookies)) |
1 |
{ |
1 |
<span style="color: #0000ff">return</span>; |
1 |
} |
1 |
  |
1 |
var transformer = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager; |
1 |
<span style="color: #0000ff">if</span> (transformer != <span style="color: #0000ff">null</span>) |
1 |
{ |
1 |
var transformedPrincipal = transformer.Authenticate(context.Request.RawUrl, context.User <span style="color: #0000ff">as</span> ClaimsPrincipal); |
1 |
  |
1 |
<span style="color: #008000">//set new principal in http context and thread current principal</span> |
1 |
context.User = transformedPrincipal; |
1 |
Thread.CurrentPrincipal = transformedPrincipal; |
1 |
} |
1 |
} |
1 |
  |
1 |
<span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> Dispose() { } |
1 |
} |
Si observa, en lineas generales, con un poquito más de amor, hemos hecho el mismo trabajo que anteriormente, asignar al hilo ( y en el caso de la web al HttpContext ) el principal obtenido de la transformación. Por supuesto, una vez creado tenemos que registrar este módulo dentro de nuestro sitio, adicionalmente, también tenemos que hacer el registro del móduglo SessionAuthenticationModule, ahora veremos para que..
1 |
<system.webServer> |
1 |
<modules runAllManagedModulesForAllRequests=<span style="color: #006080">"true"</span>> |
1 |
<add name =<span style="color: #006080">"ClaimsTransformationModule"</span> type=<span style="color: #006080">"ClaimsTranformation.ClaimsTransformationHttpModule,ClaimsTranformation"</span>/> |
1 |
<add name=<span style="color: #006080">"SessionAuthenticationModule"</span> type=<span style="color: #006080">"System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"</span>/> |
1 |
<!--<add name=<span style="color: #006080">"WSFederationAuthenticationModule"</span> type=<span style="color: #006080">"System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"</span>/>--> |
1 |
</modules> |
1 |
<validation validateIntegratedModeConfiguration=<span style="color: #006080">"false"</span> /> |
1 |
</system.webServer> |
Bien, supongamos que ya hemos hecho todo el trabajo. Si navegamos un poco por el sitio, nos daremos cuenta de que el método Authenticate es llamado de forma continua, si, por ejemplo, hacemos lo que comentamos anteriormente de buscar en nuestro repositorio claims adicionales el impacto de rendimiento sería alto. Para evitarlo, podemos guardar en sesión este principal de tal forma que no sea ejecutado tan frecuentemente. Para ello, modificaremos nuestro ClaimsAuthenticationManager para incluir lo siguiente
1 |
<span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> PTTransformer |
1 |
: ClaimsAuthenticationManager |
1 |
{ |
1 |
<span style="color: #0000ff">public</span> PTTransformer() { } |
1 |
  |
1 |
<span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> ClaimsPrincipal Authenticate(<span style="color: #0000ff">string</span> resourceName, ClaimsPrincipal incomingPrincipal) |
1 |
{ |
1 |
<span style="color: #0000ff">if</span> (incomingPrincipal != <span style="color: #0000ff">null</span> && incomingPrincipal.Identity.IsAuthenticated) |
1 |
{ |
1 |
var identity = ((ClaimsIdentity)incomingPrincipal.Identity); |
1 |
identity.AddClaim(<span style="color: #0000ff">new</span> Claim(<span style="color: #006080">"time"</span>, DateTime.Now.ToLongTimeString())); |
1 |
  |
1 |
incomingPrincipal = <span style="color: #0000ff">new</span> ClaimsPrincipal(identity); |
1 |
  |
1 |
<span style="color: #0000ff">if</span> (HttpContext.Current != <span style="color: #0000ff">null</span>) |
1 |
{ |
1 |
var token = <span style="color: #0000ff">new</span> SessionSecurityToken(incomingPrincipal); |
1 |
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(token); |
1 |
  |
1 |
} |
1 |
1 |
} |
1 |
  |
1 |
<span style="color: #0000ff">return</span> incomingPrincipal; |
1 |
} |
1 |
} |
Como podrá observar se hace uso del modulo SessionAuthenticationModule para emitir una cookie que será posteriormente utilizada para evitar el proceso de transformación de nuevo.
Bueno, hasta aquí llegaremos por hoy, espero que os esté resultando interesante..
Saludos
Unai
En la anterior entrega de esta serie vimos como extender el mecanismo de claims para hacer uso de un