【问题标题】:Transforming claims with Windows Azure Active Directory (WAAD)使用 Windows Azure Active Directory (WAAD) 转换声明
【发布时间】:2013-11-26 18:33:54
【问题描述】:

我目前在我的 MVC.NET 应用程序中使用 windows azure 活动目录作为单点登录,这部分效果很好。我可以针对 WAAD 进行身份验证并毫无问题地加载我的 ClaimsPrinical。

下一步是通过添加来自不同数据源的新声明来转换从 WAAD 检索到的声明。为此,我创建了一个继承 ClaimsAuthenticationManager 的类(如下)。声明被添加到 Principal 并在 CreateSession 方法中持久化到会话 cookie。

我现在的问题是 ClaimsPrincipal.Current 不包含我添加的任何其他声明。当我在 SessionAuthenticationModule_SessionSecurityTokenReceived 事件中设置断点时,我可以看到 ClaimsPrincipal.Current 之间存在差异

ClaimsPrincipal.Current.FindAll(ClaimTypes.Email)
Count = 0

和 e.SessionToken.ClaimsPrincipal。

e.SessionToken.ClaimsPrincipal.FindAll(ClaimTypes.Email)
Count = 1
[0]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress: me@mydomain.com}

我在这里缺少什么?在所有处理转换声明的示例中,我发现没有提到从 cookie 中手动重新加载 ClaimsPrincipal。会话安全令牌事件是否是重新加载 ClaimsPrincipal 的正确位置,还是我破坏了安全模型?

谢谢。

public class MyAuthenticationManager : ClaimsAuthenticationManager
{
    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        if (!incomingPrincipal.Identity.IsAuthenticated)
        {
            return base.Authenticate(resourceName, incomingPrincipal);
        }

        var transformedPrincipal = this.CreateUserPrincipal(incomingPrincipal.Identity.Name);
        this.CreateSession(transformedPrincipal);

        return transformedPrincipal;
    }

    private ClaimsPrincipal CreateUserPrincipal(String userName)
    {
        List<Claim> claims = new List<Claim>();
        var user = SecurityController.GetUserIdentity(userName);

        claims.Add(new Claim(ClaimTypes.Name, userName));
        claims.Add(new Claim(ClaimTypes.Email, user.Email));
        claims.Add(new Claim(ClaimTypes.GivenName, user.FirstName));
        claims.Add(new Claim(ClaimTypes.Surname, user.LastName));

        return new ClaimsPrincipal(new ClaimsIdentity(claims, "MyCustom"));
    }

    private void CreateSession(ClaimsPrincipal transformedPrincipal)
    {
        var sessionSecurityToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));

        if (FederatedAuthentication.SessionAuthenticationModule != null &&
        FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies))
        {
            return;
        }
        FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
        //Added line below as per suggestion in one of the posts
        //Doesn't seem to have any effect
        Thread.CurrentPrincipal = transformedPrincipal;
        FederatedAuthentication.SessionAuthenticationModule.SessionSecurityTokenReceived += SessionAuthenticationModule_SessionSecurityTokenReceived;
    }

    void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("SessionAuthenticationModule_SessionSecurityTokenReceived");
    }

【问题讨论】:

  • MyAuthenticationManager 是否在应用程序的 .config 文件中配置?
  • 您的测试有缺陷,因为您试图查找带有字符串“Email”的声明,它应该是 ClaimTypes.Email 或“schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
  • 是的,MyAuthenticationManager 是在 .config 文件中配置的。 Authenticate方法被调用,我可以单步执行。
  • 是的,测试有缺陷,我将它更新为 ClaimTypes.Email 但它仍然是相同的行为。我选择了电子邮件,但我还有其他正在测试的自定义声明,所以从这个角度来看没有任何变化。

标签: asp.net-mvc cookies azure claims


【解决方案1】:

看来我必须通过 ClaimsIdentity 而不是 ClaimsPrincipal 访问声明。现在我可以从应用程序中的任何视图或控制器成功访问声明。

((ClaimsIdentity)Thread.CurrentPrincipal.Identity).FindAll(ClaimTypes.Email)
Count = 1
    [0]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress: me@mydomain.com}

AuthenticationManager 中的最终代码库如下所示(请注意,当前线程上没有对 ClaimsPrincipal 的显式赋值操作)。

public class MyAuthenticationManager : ClaimsAuthenticationManager
{
    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        if (!incomingPrincipal.Identity.IsAuthenticated)
        {
            return base.Authenticate(resourceName, incomingPrincipal);
        }

        var transformedPrincipal = this.CreateUserPrincipal(incomingPrincipal.Identity.Name);
        this.CreateSession(transformedPrincipal);

        return transformedPrincipal;
    }

    private ClaimsPrincipal CreateUserPrincipal(String userName)
    {
        List<Claim> claims = new List<Claim>();
        var user = SecurityController.GetUserIdentity(userName);

        claims.Add(new Claim(ClaimTypes.Name, userName));
        claims.Add(new Claim("UserId", user.Id.ToString()));
        claims.Add(new Claim(ClaimTypes.Email, user.Email));
        claims.Add(new Claim(ClaimTypes.GivenName, user.FirstName));
        claims.Add(new Claim(ClaimTypes.Surname, user.LastName));
        //claims.Add(new Claim(ClaimTypes.NameIdentifier, userName));

        if (user.Account != null)
        {
            claims.Add(new Claim("AccountId", user.Account.Id.ToString()));
            claims.Add(new Claim("AccountName", user.Account.Name.ToString()));
        }
        if (user.Owner != null)
        {
            claims.Add(new Claim("OwnerId", user.Owner.Id.ToString()));
            claims.Add(new Claim("OwnerName", user.Owner.Name.ToString()));
        }

        return new ClaimsPrincipal(new ClaimsIdentity(claims, "MyCustom"));
    }

    private void CreateSession(ClaimsPrincipal transformedPrincipal)
    {
        if (FederatedAuthentication.SessionAuthenticationModule != null &&
        FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies))
        {
            return;
        }
        var sessionSecurityToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));
        FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
    }
}

【讨论】:

    【解决方案2】:

    我没有看到您将 ClaimsPrincipal(在转换之后)添加回 Thread.CurrentPrincipal。请尝试

    private void CreateSession(ClaimsPrincipal transformedPrincipal)
        {
            var sessionSecurityToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));
    
            if (FederatedAuthentication.SessionAuthenticationModule != null &&
            FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies))
            {
                return;
            }
            FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
            //Following is the missing line of code.
            Thread.CurrentPrincipal = transformedPrincipal;
       }
    

    【讨论】:

    • 我将主体分配给 CurrentPrincipal,但这没有任何效果。我将尝试再次按照文章构建一个单独的 POC 解决方案。
    • 您确定 cookie 已写入且代码未从以下代码返回 FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies)) { return; }
    • 您也可以尝试使用以下 SessionAuthenticationModule sam = FederatedAuthentication.SessionAuthenticationModule; 编写 cookie var st= sam.CreateSessionSecurityToken(transformedPrincipal, null, validFrom, validTo, isPersistantCookie);sam.WriteSessionTokenToCookie(st);
    • 是的,正在写入 cookie。我在“返回”部分设置了断点,它永远不会被击中。我还可以通过 chrome 中的开发人员工具查看 cookie。我尝试根据您的建议更改代码以编写 cookie,但这并没有影响任何事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-25
    • 1970-01-01
    • 2020-01-09
    • 2020-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多