我正在修改使用基于表单的身份验证(FBA)的SharePoint 2013的现有系统,以允许用户自助服务创建帐户和管理管理。他们可以管理的一件事是角色成员资格;有效地,它们被给出了它们向系统提供的角色访问代码,它将它们添加到角色中,允许它们访问角色已授予访问权限的站点。部分解决方案是自定义登录屏幕,以帮助支持所有此项,并使其更加用户友好。所有这一切都在工作相对良好。

我正在处理它的任务,为外部身份验证提供者提供支持,而不是基于表单的身份验证 - 这是一个业务要求。有很多在线信息(例如 http://blogs.msdn.com/b/mvpawardprogram/archive/2011/06/17/mvps-for-sharepoint-2010-使用-Azure-acs-v2-to-authenticate-extends-usersued-seruess-serubes.aspx )如何将Azure ACS添加为身份提供商并且正在工作。问题现在是如何添加自助服务角色授权件。

我已经在 http://www.microsoft.com/en-us/download/details.aspx?id=27569 。我击中的问题是“常见的”问题在令牌上进行缓存,所以当用户的角色成员身份更改时,他们仍然可以获得旧的缓存令牌。我的索赔增强代码未调用(当然不是 - 为什么STS使用缓存的票证时,因此未看到成员资格的更改。

这几乎是在查看权限显示不正确的信息< / a>和 http://www.shillier.com/archive/2010/10/25/authorization-failures-with-claims-based-authentication-in-in-hepoint-2010.aspx ,带有常见答案改变给出的令牌寿命。我有一些问题和问题。

  1. 在外部权利要求中,应用了什么寿命?它不是一个Windows令牌,它不是FBA令牌,所以既不是世代ocodeTagcode也不似乎是合适的,虽然那些是每个人都说改变的那些。

  2. 我不希望令牌在一个不合理的短时间内到期,我只希望圣诞节过期缓存版本或理想情况下从不缓存它(我知道那里有一个性能问题 - 我不在乎,因为我们的规模无关紧要)。然而,似乎这两件事是耦合的。有没有办法解耦它们?

  3. 如果我不能解耦,有没有办法登录屏幕向系统指示我希望索赔要重新增强?我可以通过修改当前的用户身份来我自己吗?这当然是在系统的背后,所以如果我可以帮助它,我不想这样做。

  4. 所有这一切似乎最终归结为在OOB ST中的缓存,因为所有其他动作的部件似乎都是正确的。避免这种情况是创建没有缓存行为的自定义STS并将其注册为身份提供者的自定义STS? (如果我有一个定制的STS,那么我不会在那一点上不打扰索赔增强,我刚刚首先发出所有正确的权利要求。)似乎是大量的工作(虽然我知道特别是有一个样本包括 http://archive.msdn.microsoft.com/selfsts ),特别是似乎我必须将依赖方连接到Azure ACS,因此作为过程的一部分,其中OOB STS与几行PowerShell和一些努力进行了“神奇地”。

  5. 或者,我是否需要放弃声明并简单地改变FBA侧?这感觉就像一个可怕的方式,因为现在我需要在我的身份验证代码中连接所有OpenID的东西,因为我知道我应该搬到索赔时,我向后向后移动。

    在FBA模型中,我可以通过强制用户再次登录,立即无法接受,这是一个延迟,这是不可接受的。

有帮助吗?

解决方案

好的,我得到了这个工作(yay!),但男人,这很难。

基本上,您需要强制从Sts重新发出令牌,这会强制索赔增强代码再次运行。在我的情况下,业务要求比这更复杂,因为我需要实际上只有一个可能的权利要求,但这就是在这个问题的立即范围之外。

本质上,我离开了 https://stackoverflow.com/questions/8070456/re-calculate-claims-for-sharepoint-2010-user- whele-the-user-is-still-logged -in / 8185646#8185646 。我要做的一件事是删除e.ReissueCookie,因为这完全打破了我的身份验证(用户被该操作记录)。在我的情况下,我不想通过一个特殊页面完成,所以我在应用程序集合中使用了一个特定于用户的价值,我在行动后立即清除。我的事件接收器中的相关代码在global.asax中添加和删除代码:

        private readonly string AsaxText = @"
<%@ Assembly Name=""" + System.Reflection.Assembly.GetAssembly(typeof(ClaimSupport)).FullName + @"""%>
<script runat='server'> 
    void SessionAuthentication_SessionSecurityTokenReceived(object sender, Microsoft.IdentityModel.Web.SessionSecurityTokenReceivedEventArgs e) { 
        var application = HttpContext.Current.Application;
        if (application == null) {
            return;
        }
        var key = Company.SharePoint.Authentication.Data.ClaimSupport.ApplicationEventKey(e.SessionToken.ClaimsPrincipal.Identity.Name);
        var applicationValue = application[key];
        if (applicationValue == null) { 
            return;
        }
        var applicationString = applicationValue.ToString();
        if (string.IsNullOrWhiteSpace(applicationString)) {
            return;
        }
        application[key] = null;
        var sam = sender as Microsoft.IdentityModel.Web.SessionAuthenticationModule; 
        var logonWindow = Microsoft.SharePoint.Administration.Claims.SPSecurityTokenServiceManager.Local.LogonTokenCacheExpirationWindow; 
        var newValidTo = System.DateTime.UtcNow.Add(logonWindow);
        var currentPrincipal = e.SessionToken.ClaimsPrincipal;
        var claimsIdentity = (Microsoft.IdentityModel.Claims.IClaimsIdentity)currentPrincipal.Identity;

        var heartbeatClaim = GetHeartbeatClaim(claimsIdentity);
        var issuer = heartbeatClaim.Issuer;
        var originalIssuer = heartbeatClaim.OriginalIssuer;
        RemoveExistingEventClaims(claimsIdentity);
        AddEventClaim(claimsIdentity, applicationString, issuer, originalIssuer);

        e.SessionToken = sam.CreateSessionSecurityToken( 
            currentPrincipal, 
            e.SessionToken.Context, 
            e.SessionToken.ValidFrom, 
            newValidTo, 
            e.SessionToken.IsPersistent); 
        //e.ReissueCookie = true; - commented out because it broke things for me, but kept for reference
    } 

    private Microsoft.IdentityModel.Claims.Claim GetHeartbeatClaim(Microsoft.IdentityModel.Claims.IClaimsIdentity claimsIdentity) {
        var heartbeatClaim = (from c in claimsIdentity.Claims
                              where
                                  (c.ClaimType == Company.SharePoint.Authentication.Data.ClaimSupport.EventClaimType)
                              &&
                                  (c.Value == Company.SharePoint.Authentication.Data.ClaimSupport.HeartbeatClaimValue)
                              select c).FirstOrDefault();
        return heartbeatClaim;
    }

    private void AddEventClaim(Microsoft.IdentityModel.Claims.IClaimsIdentity claimsIdentity, string additionalEvent, string issuer, string originalIssuer) {
        var eventClaim = new Microsoft.IdentityModel.Claims.Claim(Company.SharePoint.Authentication.Data.ClaimSupport.EventClaimType, additionalEvent, HynesITe.SharePoint.Authentication.Data.ClaimSupport.EventClaimValueType, issuer, originalIssuer);
        claimsIdentity.Claims.Add(eventClaim);
    }

    private static void RemoveExistingEventClaims(Microsoft.IdentityModel.Claims.IClaimsIdentity claimsIdentity) {
        var currentClaims = (from c in claimsIdentity.Claims
                             where
                                 (c.ClaimType == HynesITe.SharePoint.Authentication.Data.ClaimSupport.EventClaimType)
                             &&
                                 (c.Value != HynesITe.SharePoint.Authentication.Data.ClaimSupport.HeartbeatClaimValue)
                             select c).ToList();
        foreach (var claim in currentClaims) {
            claimsIdentity.Claims.Remove(claim);
        }
    }
</script> 
";

        private void AddGlobalAsax(SPFeatureReceiverProperties properties) {
            var webApp = properties.Feature.Parent as SPWebApplication;
            if (webApp == null) {
                throw new SPException("Cannot add global.asax entries.");
            }
            var zones = Enum.GetValues(typeof(SPUrlZone)).Cast<SPUrlZone>().ToArray();
            var paths =
                zones.Select(z => Path.Combine(webApp.GetIisSettingsWithFallback(z).Path.ToString(), "global.asax"))
                    .Distinct().Where(File.Exists).ToArray();
            var globalAsaxFiles = new List<string>();
            globalAsaxFiles.AddRange(paths);
            foreach (var asax in from asax in globalAsaxFiles
                                 let contents = File.ReadAllText(asax)
                                 where !contents.Contains(AsaxText)
                                 select asax) {
                File.AppendAllText(asax, AsaxText);
            }
        }

        private void RemoveGlobalAsax(SPFeatureReceiverProperties properties) {
            var webApp = properties.Feature.Parent as SPWebApplication;
            if (webApp == null) {
                throw new SPException("Cannot add global.asax entries.");
            }
            var zones = Enum.GetValues(typeof(SPUrlZone)).Cast<SPUrlZone>().ToArray();
            var paths =
                zones.Select(z => Path.Combine(webApp.GetIisSettingsWithFallback(z).Path.ToString(), "global.asax"))
                    .Distinct().Where(File.Exists).ToArray();
            var globalAsaxFiles = new List<string>();
            globalAsaxFiles.AddRange(paths);
            foreach (var asax in globalAsaxFiles) {
                var contents = File.ReadAllText(asax);
                if (contents.Contains(AsaxText)) {
                    var replaced = contents.Replace(AsaxText, string.Empty);
                    File.WriteAllText(asax, replaced);
                }
            }
        }

        public override void FeatureActivated(SPFeatureReceiverProperties properties) {
            // other stuff
            AddGlobalAsax(properties);
            // other stuff
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
            // other stuff
            RemoveGlobalAsax(properties);
            // other stuff
        }
.

ClaimSupport汇编包含一些杂项共享代码:

namespace HynesITe.SharePoint.Authentication.Data {
    public static class ClaimSupport {
        public static string EventClaimType {
            get {
                return "http://schema.Company.com/events";
            }
        }

        public static string EventClaimValueType {
            get {
                return Microsoft.IdentityModel.Claims.ClaimValueTypes.String;
            }
        }

        public static string ApplicationEventKey(string username) {
            return username + ":CurrentEvent";
        }

        public static string ApplicationIssuerKey(string username) {
            return username + ":Issuer";
        }

        public static string ApplicationOriginalIssuerKey(string username) {
            return username + ":OriginalIssuer";
        }

        public static string HeartbeatClaimValue {
            get {
                return "[heartbeat]";
            }
        }
    }
}
.

这里真正重要的是,如果您正在操纵索赔,正如我在这里,并且您希望SharePoint识别出来的声称,您需要匹配SharePoint将与盗窃者一起使用的发行版信息,这是为什么“心跳”索赔。在大多数情况下,您将不关心任何一个 - 索赔增强器将做正确的事情 - 但在这里,我需要只有一个可能的索赔集之一,所以我必须直接在此代码中操纵索赔增强代码。

我也知道你可以将命名空间导入生成的,但我担心该文件的其他代码或用户操纵,因此我尽可能独立地进行了更改。

许可以下: CC-BY-SA归因
scroll top