简单的声明转换和缓存与Windows身份验证

时间:2013-06-03 19:45:21

标签: .net .net-4.5 wif claims-based-identity

在过去的几天里,我一直在阅读关于Windows身份基础以及它如何如此优秀和灵活并且内置于.net 4.5的内容。尽管经历了几十个api,博客文章,操作方法等等,但我无法为我的生活做一个简单的实现工作。

我只使用Windows身份验证,我可以获取主体并查看随附的声明(这是每个示例似乎结束的地方)。但是,我想将它们转换为有用的声明并缓存结果,以便每次请求都不会发生转换。

在我的web.config中,我有:

  <configSections>
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  </configSections>

  <system.identityModel>
    <identityConfiguration>
      <claimsAuthenticationManager type="SecurityProj.MyClaimsTransformationModule,SecurityProj" />
      <claimsAuthorizationManager type="SecurityProj.MyClaimsAuthorizationManager,SecurityProj" />
    </identityConfiguration>
  </system.identityModel>

然而,身份验证管理器永远不会被调用。我可以通过添加:

来获得它的唯一方法
protected void Application_PostAuthenticateRequest()
{
    ClaimsPrincipal currentPrincipal = ClaimsPrincipal.Current;
    ClaimsTransformationModule customClaimsTransformer = new MyClaimsTransformationModule();
    ClaimsPrincipal tranformedClaimsPrincipal = customClaimsTransformer.Authenticate(string.Empty, currentPrincipal);
    HttpContext.Current.User = tranformedClaimsPrincipal;
}

到我的global.asax.cs文件。它适用于第一个请求,但之后我得到“安全句柄已关闭”错误,并且不知道是什么导致它。显然,这不是正确的方法,所以有谁知道什么是最好或简单的工作实践?这仅适用于Windows身份验证,我不需要更复杂的东西。

对于缓存,我试图使用:

        SessionSecurityToken token = FederatedAuthentication.SessionAuthenticationModule
            .CreateSessionSecurityToken(
            currentPrincipal,
            "Security test",
            System.DateTime.UtcNow,
            System.DateTime.UtcNow.AddHours(1),
            true);

        if (FederatedAuthentication.SessionAuthenticationModule != null &&
            FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies))
        {
            return;
        }
        FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(token);

但我也不确定那部分,转换问题需要先解决。

任何帮助将不胜感激。只需要调用查找/转换和cookie集,谢谢。

2 个答案:

答案 0 :(得分:19)

我现在已经完成了所有工作,以下是我的工作方式:

在此页面上:http://msdn.microsoft.com/en-us/library/ee517293.aspx是关键段落:

  

如果您想让您的RP应用程序声明感知,但您没有STS(例如,RP使用Forms身份验证或Windows集成身份验证),则可以使用ClaimsPrincipalHttpModule。该模块位于应用程序的HTTP管道中,并拦截身份验证信息。它根据用户的用户名,组成员身份和其他身份验证信息为每个用户生成IClaimsPrincipal。 ClaimsPrincipalHttpModule必须插入<httpModules>管道的末尾,这是IIS 7上<modules>的{​​{1}}部分中的第一个元素。

这页:

http://leastprivilege.com/2012/04/04/identity-in-net-4-5part-2-claims-transformation-in-asp-net-beta-1/

给你全班:

<system.webServer>

现在将该类添加到web.config:

public class ClaimsTransformationHttpModule : IHttpModule
{
    public void Dispose()
    { }

    public void Init(HttpApplication context)
    {
        context.PostAuthenticateRequest += Context_PostAuthenticateRequest;
    }

    void Context_PostAuthenticateRequest(object sender, EventArgs e)
    {
        var context = ((HttpApplication)sender).Context;

        // no need to call transformation if session already exists
        if (FederatedAuthentication.SessionAuthenticationModule != null &&
            FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(context.Request.Cookies))
        {
            return;
        }

        var transformer =
        FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager;
        if (transformer != null)
        {
            var transformedPrincipal = transformer.Authenticate(context.Request.RawUrl, context.User as ClaimsPrincipal);

            context.User = transformedPrincipal;
            Thread.CurrentPrincipal = transformedPrincipal;
        }
    }
}

现在它将调用转换,我可以删除global.asax中的post authenticate方法。

在authenticate方法中,我调用它来设置cookie:

<modules>
  <add name="ClaimsTransformationHttpModule" type="TestSecurity.ClaimsTransformationHttpModule" />
</modules>

之前的模块已经设置为查看它并跳过身份验证(如果存在)。

最后是我一直得到的安全句柄错误。我不确定原因,但我发现如果我修改了传递给Authenticate然后返回它的主体(它在msdn上显示的内容),那么错误将显示在所有后续请求中。但是,如果我创建并返回一个新的主体,那么它就不会发生。这对于删除不需要的声明也很有用。

private void CreateSession(ClaimsPrincipal transformedPrincipal)
{
    SessionSecurityToken sessionSecurityToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));
    FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
}

所以现在我可以对Windows进行身份验证,引入自定义声明,并使用cookie缓存它们。希望这有助于其他人尝试这样做,如果我没有做正确的事,请告诉我。

答案 1 :(得分:10)

这个答案旨在进一步澄清约翰上面的答案,经历了几个令人沮丧的日子,试图解决一些类似的问题。

1。 ClaimsPrincipalHttpModule

正如John所发现的,如果您使用的是Windows Auth或Forms Auth,ASP.NET将不会自动调用您的ClaimsAuthenticationManager(它不是联合方案)。在ASP.NET对用户进行身份验证后,您必须自己执行此操作。使用ClaimsPrincipalHttpModule(曾经是IdentityModel的一部分)将有效地确保发生这种情况。

但是,请谨慎使用此模块。它被移除是有原因的。我从IdentityModel中删除它的理由是因为如果你在ASP.NET应用程序中托管WCF服务并且aspNetCompatibilityEnabled = true,它就不能很好地运行。它将导致您的WCF身份验证中断(模块将在WCF管道之前执行,我的经验是您的WCF客户端将无法再正确进行身份验证 - 我在使用Windows Auth时已经确认了这一点)。

如果您在此方案中托管WCF服务,则必须以某种方式确保仅针对非WCF请求调用ClaimsAuthenticationManager。对于WCF请求,您似乎必须依赖WCF管道(<serviceCredentials useIdentityConfiguration="true" />)。最简单的解决方法是简单地关闭aspNetCompatibilityEnabled。如果这不是一个选项,则不应使用ClaimsPrincipalHttpModule,但必须以某种方式检查传入请求,并且只有在请求不是以WCF为目的地时才调用ClaimsAuthenticationManager。

2。安全句柄错误(ObjectDisposedException)

如果您创建基于WindowsIdentity的SessionSecurityToken,则会发生这种情况。 SessionAuthenticationModule具有处理从SessionSecurityToken读取的WindowsIdentity声明的特殊逻辑,并将尝试使用不再有效的数据重新水化WindowsIdentity。 (我不确定它会起作用的情况,但在我测试的所有场景中它总是失败)。因此,正如John解释的那样,这里的教训是,当尝试在Windows身份验证中使用WIF时,不应从WindowsPrincipal(或更正确的WindowsIdentity)创建SessionSecurityTokens。任何其他类型的转换的ClaimsPrincipal应该没问题。