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

Joh*_*ohn 20 .net claims-based-identity wif .net-4.5

在过去的几天里,我一直在阅读关于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>
Run Code Online (Sandbox Code Playgroud)

但是,认证管理器永远不会被调用.我可以通过以下方式添加:

protected void Application_PostAuthenticateRequest()
{
    ClaimsPrincipal currentPrincipal = ClaimsPrincipal.Current;
    ClaimsTransformationModule customClaimsTransformer = new MyClaimsTransformationModule();
    ClaimsPrincipal tranformedClaimsPrincipal = customClaimsTransformer.Authenticate(string.Empty, currentPrincipal);
    HttpContext.Current.User = tranformedClaimsPrincipal;
}
Run Code Online (Sandbox Code Playgroud)

到我的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);
Run Code Online (Sandbox Code Playgroud)

但我也不确定那部分,转换问题需要先修复.

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

Joh*_*ohn 19

我现在一切都在运作,这就是我如何去做的:

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

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

而这个页面:

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

给你全班:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<modules>
  <add name="ClaimsTransformationHttpModule" type="TestSecurity.ClaimsTransformationHttpModule" />
</modules>
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

List<Claim> newClaims = new List<Claim>();

var keeper = ((ClaimsIdentity)incomingPrincipal.Identity).Claims.First(c =>
    c.Type == ClaimTypes.Name);
newClaims.Add(keeper);

ClaimsIdentity ci = new ClaimsIdentity(newClaims, "Negotiate");

return new ClaimsPrincipal(ci);
Run Code Online (Sandbox Code Playgroud)

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


Phi*_*rdt 11

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

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都应该没问题.