[c#] WCF-Authentifizierung mit benutzerdefinierten ClientCredentials: Was ist der zu verwendende clientCredentialType?


Answers

Question

Ich musste die grundlegende WCF UserName / Pwd-Sicherheit ablegen und meine eigenen benutzerdefinierten Clientanmeldeinformationen implementieren, um weitere Informationen zu speichern, die über das hinausgehen, was standardmäßig bereitgestellt wird.

Ich habe diesen MSDN-Artikel durchgearbeitet, aber mir fehlt etwas, weil es nicht funktioniert.

Zuerst habe ich einige benutzerdefinierte ClientCredentials, die einen benutzerdefinierten ClientCredentialsSecurityTokenManager bereitstellen:

public class CentralAuthCredentials : ClientCredentials
{
    public override System.IdentityModel.Selectors.SecurityTokenManager CreateSecurityTokenManager()
    {
        return new CentralAuthTokenManager(this);
    }
}

public class CentralAuthTokenManager : ClientCredentialsSecurityTokenManager
{
    private CentralAuthCredentials credentials;

    public CentralAuthTokenManager(CentralAuthCredentials creds) : base(creds)
    {
        this.credentials = creds;
    }

    public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
    {
        if (this.IsIssuedSecurityTokenRequirement(tokenRequirement) || tokenRequirement.TokenType == CentralAuthToken.TOKEN_TYPE)
            return new CentralAuthTokenProvider(credentials.UserId, credentials.UserPassword, credentials.ImpersonateId, credentials.LoginType);
        else
            return base.CreateSecurityTokenProvider(tokenRequirement);
    }

    public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
    {
        outOfBandTokenResolver = null;
        if (this.IsIssuedSecurityTokenRequirement(tokenRequirement) || tokenRequirement.TokenType == CentralAuthToken.TOKEN_TYPE)
            return new CentralAuthTokenAuthenticator();
        else
            return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
    }

    public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
    {
        return new CentralAuthTokenSerializer();
    }
}

Wenn ich die App jetzt ausführe, werden meine benutzerdefinierten Anmeldeinformationen und der Token-Manager erstellt. Jedoch in der Methode:

CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
{
    ...
}

Der TokenRequirement.TokenType ist etwas anderes als mein benutzerdefiniertes Token. Das bringt meine erste Frage auf : Wie zum Teufel weiß WCF, was die Token-Anforderungen sind?

Auch die Methode:

public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
{
    return new CentralAuthTokenSerializer();
}

Wird vom Client einmal aufgerufen, aber keine der Methoden auf dem zurückgegebenen Token-Serializer wird jemals aufgerufen. Dies zeigt mir an, dass das benutzerdefinierte Token nie über die Leitung gesendet wird. Ich nehme an, dass das ist, weil der Aufruf von CreateSecurityTokenProvider () meinen benutzerdefinierten Token-Anbieter nie zurückgab, da das SecurityTokenRequirement nie übergeben wird, das angibt, dass mein benutzerdefiniertes Token benötigt wird.

Auf der Clientseite habe ich:

public class CentralAuthorizationManagerClient : ClientBase<ICentralAuthorizationManager>, ICentralAuthorizationManager, IDisposable
{
    public PFPrincipal GenerateToken()
    {
        if (!this.ChannelFactory.Endpoint.Behaviors.Contains(typeof(CentralAuthCredentials)))
            throw new ArgumentException("Must set CentralAuthCredentials before calling this method.");
        return base.Channel.GenerateToken();
    }

    public PFPrincipal GenerateToken(CentralAuthToken token)
    {
        this.ChannelFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
        this.ChannelFactory.Endpoint.Behaviors.Add(new CentralAuthCredentials(token));
        return this.GenerateToken();
    }

Diese Methoden sollen im Grunde die Standardanmeldeinformationen vom Endpunkt entfernen und eine neue Instanz meiner benutzerdefinierten CentralAuthCredentials anhängen. (Ich habe diese Remove / Add-Combo aus einem MSDN-Artikel irgendwo abgeholt).

In der Konfiguration:

    <behaviors>
        <endpointBehaviors>
            <behavior name="Server2ServerEndpointBehavior">
                <clientCredentials type="MyApp.Security.CentralAuthCredentials, MyApp">
                    <clientCertificate findValue="localhost" x509FindType="FindBySubjectName" storeLocation="CurrentUser" storeName="My" />
                    <serviceCertificate>
                        <authentication certificateValidationMode="None" revocationMode="NoCheck" />
                    </serviceCertificate>
                </clientCredentials>
            </behavior>
        </endpointBehaviors>
    </behaviors>

    <bindings>
        <wsHttpBinding>
            <binding name="wsHttpServer2Server">
                <security mode="Message">
                    <message clientCredentialType="UserName" />
                </security>
            </binding>
        </wsHttpBinding>
    </bindings>

Beachten Sie, dass der Typ clientCredentials des Verhaltens auf meine benutzerdefinierten Clientanmeldeinformationen festgelegt ist. Im Moment habe ich jedoch noch den clientCredentialType der Bindung auf "UserName" gesetzt. Dies bringt meine zweite Frage auf : Was zum Teufel sollte clientCredentialType = "?" festgelegt werden, wenn ich benutzerdefinierte Anmeldeinformationen verwende? Laut MSDN sind die verfügbaren Werte für die Nachrichtensicherheit : Keine , Windows , Benutzername , Zertifikat und IssuedToken .

Irgendwelche Ideen? Hoffentlich vermisse ich einfach etwas Einfaches? Es gibt 6 weitere Klassen für die gesamte Implementierung, aber ich habe versucht, nur die Bits einzubeziehen, die benötigt werden, um die Situation zu verstehen ...

UPDATE # 1:

Ich habe den ganzen Tag daran gearbeitet und dank einiger Quellen habe ich festgestellt, dass der letzte Schritt auf dieser Seite der TokenParameters ist, der der Bindung hinzugefügt wurde, damit die Bindung weiß was das Token sieht wie aus. Das ist die Antwort auf meine erste Frage. "Was zum Teufel setzt die Token-Anforderungen?" Antwort: Die der Bindung zugewiesenen TokenParameter.

So habe ich jetzt die folgende Erweiterung hinzugefügt, die die TokenParameters auf die Bindung setzt:

public sealed class CentralAuthTokenBindingExtension : BindingElementExtensionElement
{
    public CentralAuthTokenBindingExtension()
        : base()
    {
    }

    public override Type BindingElementType
    {
        get { return typeof(SymmetricSecurityBindingElement); }
    }

    protected override System.ServiceModel.Channels.BindingElement CreateBindingElement()
    {
        X509SecurityTokenParameters protectionParams = new X509SecurityTokenParameters();
        protectionParams.InclusionMode = SecurityTokenInclusionMode.Never;

        SymmetricSecurityBindingElement innerBindingElement = new SymmetricSecurityBindingElement();
        innerBindingElement.EndpointSupportingTokenParameters.SignedEncrypted.Add(new CentralAuthTokenParameters());
        //innerBindingElement.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt;
        innerBindingElement.ProtectionTokenParameters = protectionParams;

        return innerBindingElement;
    }
}

    <extensions>
        <bindingElementExtensions>
            <add name="CentralAuthCreds" type="MyApp.Security.Configuration.CentralAuthTokenBindingExtension, MyApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        </bindingElementExtensions>
    </extensions>

    <bindings>
        <customBinding>
            <binding name="wsHttpServer2Server">
                <CentralAuthCreds />
                <binaryMessageEncoding />
                <httpTransport />
            </binding>
        </customBinding>
    </bindings>

Nun, das bringt mich einen Schritt weiter. Jetzt bekomme ich eine neue Ausnahme auf dem Server:

"The security token manager cannot create a token authenticator for requirement ..."

Es sieht so aus, als ob WCF einen Standard-Token-Manager verwendet, um zu versuchen, mit meinem benutzerdefinierten Token umzugehen, anstatt mit meinem benutzerdefinierten Token-Handler (der Konstruktor meines benutzerdefinierten Token-Handlers wird nie aufgerufen). Ich denke, das passiert, weil ich für den Client diese Konfiguration habe:

<endpointBehaviors>
    <behavior name="Server2ServerEndpointBehavior">
        <clientCredentials type="MyApp.Security.CentralAuthCredentials, MyApp">

Aber auf dem Server habe ich keine Entsprechung, um es über die benutzerdefinierten Client-Anmeldeinformationen zu informieren. Also, neue Frage : Wo in der Konfig für den Server erzähle ich es, was die benutzerdefinierten ClientCredentials sind?

Update # 2:

Nun, ich habe endlich ein bisschen mehr von dem Puzzle herausgefunden. Ich hatte nur eine ClientCredentials-Implementierung implementiert und dachte, dass der Kunde Kredits sendet, und das ist es. Der Client authentifiziert den Dienst nicht, daher benötige ich keine benutzerdefinierten ServiceCredentials. Nun, ich habe mich geirrt. Die angegebenen ServiceCredentials authentifizieren das Token von den ClientCredentials und umgekehrt. Daher musste ich nur eine benutzerdefinierte ServiceCredentials-Implementierung hinzufügen, die dieselben Klassen TokenSerializer und TokenAuthenticator übergab.

Zum nächsten Problem: WCF ignoriert jetzt meine in Config angegebenen x509-Zertifikate, die mit der UserName-Authentifizierung funktionierten. Ich werde eine ganz neue Frage für diesen eröffnen!




Links