Frage

Ich ändere ein bestehendes System, das SharePoint 2013 mit formularbasierter Authentifizierung (FBA) verwendet, um Benutzern die Self-Service-Erstellung von Konten und die Verwaltung der Konten zu ermöglichen.Sie können unter anderem die Rollenmitgliedschaft verwalten.Tatsächlich erhalten sie einen Rollenzugriffscode, den sie an das System weitergeben, und es fügt sie der Rolle hinzu, sodass sie auf Websites zugreifen können, auf die der Rolle Zugriff gewährt wurde.Teil der Lösung ist ein benutzerdefinierter Anmeldebildschirm, der all dies unterstützt und benutzerfreundlicher macht.Das alles klappt relativ gut.

Die Aufgabe, an der ich arbeite, besteht darin, Unterstützung für externe Authentifizierungsanbieter anstelle der formularbasierten Authentifizierung hinzuzufügen – dies ist eine Geschäftsanforderung.Es gibt zahlreiche Online-Informationen (z.G. http://blogs.msdn.com/b/mvpawardprogram/archive/2011/06/17/mvps-for-sharepoint-2010-using-azure-acs-v2-to-authenticate-external-systems-users.aspx) erfahren Sie, wie Sie Azure ACS als Identitätsanbieter hinzufügen und das funktioniert.Die Frage besteht nun darin, wie das Autorisierungselement für die Self-Service-Rolle hinzugefügt wird.

Ich habe eine einfache Anspruchserweiterung erhalten, die einwandfrei funktioniert, wenn ich der Anleitung unter folge http://www.microsoft.com/en-us/download/details.aspx?id=27569.Das Problem, auf das ich stoße, ist das „häufige“ Problem, dass der STS im Posteingang das Token zwischenspeichert. Wenn also die Rollenmitgliedschaft eines Benutzers geändert wird, erhält er weiterhin das alte zwischengespeicherte Token.Mein Anspruchserweiterungscode wird nicht aufgerufen (natürlich nicht – warum auch, wenn der STS ein zwischengespeichertes Ticket verwendet), sodass die Änderung der Mitgliedschaft nicht sichtbar ist.

Das ist so ziemlich die gleiche Art von Problem, die bei diskutiert wurde Überprüfen Sie die Berechtigungen, die falsche Informationen anzeigen Und http://www.shillier.com/archive/2010/10/25/authorization-failures-with-claims-based-authentication-in-sharepoint-2010.aspx, mit der allgemeinen Antwort, die angegebene Token-Lebensdauer zu ändern.Ich habe ein paar Probleme und Fragen dazu.

  1. Wie hoch ist die Lebensdauer im externen Schadensszenario?Es ist kein Windows-Token und es ist kein FBA-Token, also auch nicht WindowsTokenLifetime noch FormsTokenLifetime erscheinen angemessen, obwohl jeder sagt, dass dies die beiden sind, die man ändern sollte.

  2. Ich möchte nicht, dass das Token für den Endbenutzer in unangemessen kurzer Zeit abläuft. Ich möchte nur, dass der STS die zwischengespeicherte Version abläuft oder sie im Idealfall überhaupt nie zwischenspeichert (ich weiß, dass es dort ein Leistungsproblem gibt – das ist mir egal , für unsere Skala wird es keine Rolle spielen).Es scheint jedoch, als ob diese beiden Dinge miteinander verbunden sind.Gibt es eine Möglichkeit, sie zu entkoppeln?

  3. Wenn ich die Verbindung nicht trennen kann, gibt es dann eine Möglichkeit, dem System auf dem Anmeldebildschirm mitzuteilen, dass der Anspruch erneut erweitert werden soll?Kann ich es einfach selbst ändern, indem ich die aktuelle Benutzeridentität ändere?Das passiert natürlich hinter dem Rücken des Systems, also möchte ich das nicht tun, wenn ich es verhindern kann.

  4. All dies scheint letztendlich auf das Caching im OOB STS zurückzuführen zu sein, da alle anderen beweglichen Teile zu stimmen scheinen.Ist der „richtige“ Weg, dies zu vermeiden, einen benutzerdefinierten STS zu erstellen, der nicht über das Caching-Verhalten verfügt, und ihn als Identitätsanbieter zu registrieren?(Wenn ich ein benutzerdefiniertes STS habe, würde ich mich zu diesem Zeitpunkt anscheinend nicht um die Erweiterung der Ansprüche kümmern, sondern zunächst alle richtigen Ansprüche ausstellen.) Das scheint jedoch eine beträchtliche Menge an Arbeit zu sein (obwohl ich es weiß). Es gibt Beispiele einschließlich http://archive.msdn.microsoft.com/SelfSTS), insbesondere weil es so aussieht, als müsste ich dann im Rahmen des Prozesses die Verbindung der vertrauenden Seite zu Azure ACS herstellen, wo der OOB STS dies „magisch“ mit ein paar Zeilen PowerShell und einigem Aufwand erledigt.

  5. Oder muss ich Ansprüche aufgeben und einfach die FBA-Seite wechseln?Das fühlt sich wie ein schrecklicher Weg an, da ich jetzt alle OpenID-Sachen in meinen Authentifizierungscode für FBA einbinden muss und ich rückwärts von Ansprüchen weggehe, obwohl ich weiß, dass ich zu Ansprüchen übergehen sollte.

Eine Verzögerung von beispielsweise 60 Minuten ist einfach nicht akzeptabel, wenn ich im FBA-Modell die Änderung sofort vornehmen kann, indem ich den Benutzer dazu zwinge, sich erneut anzumelden, wodurch er die neue Rollenmitgliedschaft erhält.

War es hilfreich?

Lösung

OK, ich habe das zum Laufen gebracht (juhuu!), aber Mann, es war hart.

Grundsätzlich müssen Sie die erneute Ausstellung des Tokens vom STS erzwingen, was die erneute Ausführung des Anspruchserweiterungscodes erzwingt.In meinem Fall waren die Geschäftsanforderungen etwas komplizierter, da ich eigentlich nur einen der möglichen Ansprüche haben musste, aber das liegt außerhalb des unmittelbaren Rahmens dieser Frage.

Im Wesentlichen bin ich von dem unter erwähnten Code abgewichen https://stackoverflow.com/questions/8070456/re-calculate-claims-for-sharepoint-2010-user-while-the-user-is-still-logged-in/8185646#8185646 .Eine Sache, die ich tun musste, war, das zu entfernen e.ReissueCookie da dadurch die Authentifizierung für mich völlig unterbrochen wurde (der Benutzer wurde durch diese Aktion abgemeldet).In meinem Fall wollte ich das nicht über eine spezielle Seite machen, also habe ich einen benutzerspezifischen Wert in der Anwendungssammlung verwendet, den ich sofort lösche, nachdem ich darauf reagiert habe.Der relevante Code in meinem Ereignisempfänger, der den Code hinzufügt und entfernt global.asax folgt:

        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
        }

Der ClaimSupport Die Assembly enthält verschiedene gemeinsam genutzte Codes:

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

Eine Sache, die hier wirklich wichtig ist, ist, dass Sie, wenn Sie Ansprüche manipulieren, wie ich hier bin, und erwarten, dass diese Ansprüche von SharePoint erkannt werden, die Ausstellerinformationen, die SharePoint verwenden würde, mit einem SPClaim abgleichen müssen, weshalb das „ Herzschlag“-Behauptung.In den meisten Fällen wird Ihnen das alles egal sein – der Claims Augmenter wird das Richtige tun – aber hier brauche ich nur einen der möglichen Sätze von Ansprüchen, also musste ich die Manipulation direkt in diesem Code vornehmen, statt in der Anspruchserweiterungscode.

Ich weiß auch, dass Sie Namespaces importieren können global.asax Aber ich mache mir Sorgen über anderen Code oder Benutzermanipulationen an dieser Datei, deshalb habe ich die Änderungen so unabhängig wie möglich vorgenommen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit sharepoint.stackexchange
scroll top