20

在过去的几天里,我一直在阅读有关 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 集,谢谢。

4

2 回答 2

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;
        }
    }
}

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

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

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

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

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

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

最后是我不断收到的安全处理错误。我不确定原因,但我发现如果我修改了传递给 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);

所以现在我可以对 Windows 进行身份验证,引入自定义声明,并将它们与 cookie 一起缓存。希望这可以帮助其他尝试做同样的事情的人,如果我做的不对,请告诉我。

于 2013-06-06T18:52:25.387 回答
11

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

1. ClaimsPrincipalHttpModule

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

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

如果您在这种情况下托管 WCF 服务,则必须以某种方式确保仅对非 WCF 请求调用 ClaimsAuthenticationManager。对于 WCF 请求,您似乎必须依靠 WCF 管道来执行此操作(<serviceCredentials useIdentityConfiguration="true" />)。最简单的解决方法是简单地关闭 aspNetCompatibilityEnabled。如果这不是一个选项,则不应使用 ClaimsPrincipalHttpModule,而必须以某种方式检查传入请求,并且仅在请求不是发往 WCF 时才调用 ClaimsAuthenticationManager。

2. 安全处理错误(ObjectDisposedException)

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

于 2013-12-31T04:56:26.597 回答