您可以在多个地方执行此操作,但如果您使用的是SessionAuthenticationModule,则某些过程没有很好地记录,这可能会使使用自定义主体变得棘手。此答案的其余部分解释了使用SessionAuthenticationModule 时处理此问题的一种可能方法。
覆盖SessionAuthenticationModule.SetPrincipalFromSessionToken 方法。
SessionAuthenticationModule 将安全令牌、主体、身份和声明存储在 cookie 和内存缓存中,以避免在每次请求时都必须往返于身份提供者/令牌服务。没有很好记录的是缓存的存在,它是第一个检查的地方,然后是 cookie,以及序列化 ClaimsPrincipal 的限制。
如果您已经在 ClaimsAuthenticationManager.Authenticate 中设置了自定义主体并且缓存完好无损,那么您的自定义主体很可能已经存在,因为缓存存储了本机 .NET 对象。如果您尚未设置自定义主体,或者未填充缓存,则将从 FedAuth 会话 cookie 中检索安全令牌。
当令牌被序列化/反序列化到 cookie 时,进程使用自定义序列化,该序列化只能读取和写入 IClaimsPrincipal 和 IClaimsIdentity 接口(或 ClaimsPrinicpal 和 ClaimsIdentity 的属性)类 - 我不记得是哪个)。将不包括任何自定义主体和身份对象属性。可能可以覆盖序列化,但这需要几个(3 IIRC)更多的类覆盖层。
您还需要注意基本SetPrincipalFromSessionToken 方法会创建一个新的ClaimsPrincipal 对象并将其设置在线程和上下文中,因此即使sessionSecurityToken 参数包含自定义主体对象,它将被转换回ClaimsPrincipal 对象。
这是一个示例覆盖方法:
protected override void SetPrincipalFromSessionToken(SessionSecurityToken sessionSecurityToken)
{
SessionSecurityToken newToken = MyClaimsPrincipalUtility.CreateCustomClaimsPrincipalToken(sessionSecurityToken);
base.SetPrincipalFromSessionToken(newToken);
//the following lines need to be set after the base method call, because the base method overwrites the Principal object
HttpContext.Current.User = newToken.ClaimsPrincipal;
Thread.CurrentPrincipal = newToken.ClaimsPrincipal;
}
基类 (SessionAuthenticationModule) 实现如下所示。因此,有几种不同的方法可以实现覆盖和获取自定义声明主体。
protected virtual void SetPrincipalFromSessionToken(SessionSecurityToken sessionSecurityToken)
{
IClaimsPrincipal fromIdentities = ClaimsPrincipal.CreateFromIdentities(this.ValidateSessionToken(sessionSecurityToken));
HttpContext.Current.User = (IPrincipal) fromIdentities;
Thread.CurrentPrincipal = (IPrincipal) fromIdentities;
//tracing code removed
this.ContextSessionSecurityToken = sessionSecurityToken;
}
如果您想知道,这里是SessionAuthenticationModule.ContextSessionSecurityToken 的基类实现。
public virtual SessionSecurityToken ContextSessionSecurityToken
{
get
{
return (SessionSecurityToken) HttpContext.Current.Items[(object) typeof (SessionSecurityToken).AssemblyQualifiedName];
}
internal set
{
HttpContext.Current.Items[(object) typeof (SessionSecurityToken).AssemblyQualifiedName] = (object) value;
}
}