Аутентификация WCF с пользовательскими ClientCredentials. Какой тип clientCredentialType используется? - PullRequest
24 голосов
/ 19 февраля 2009

Мне пришлось отказаться от базовой безопасности WCF UserName / Pwd и реализовать свои собственные клиентские учетные данные, чтобы в них содержалось больше информации, чем предусмотрено по умолчанию.

Я работал над этой статьей MSDN , но мне чего-то не хватает, потому что она не работает.

Во-первых, у меня есть несколько пользовательских ClientCredentials, которые предоставляют собственный ClientCredentialsSecurityTokenManager:

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

Теперь, когда я запускаю приложение, мои пользовательские учетные данные и менеджер токенов создаются. Однако в методе:

CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
{
    ...
}

tokenRequirement.TokenType отличается от моего собственного токена. Это поднимает мой 1-й вопрос : Как, черт возьми, WCF знает, каковы требования к токену?

Также метод:

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

Клиент вызывается один раз, но ни один из методов возвращаемого сериализатора токенов не вызывается. Это указывает на то, что пользовательский токен никогда не отправляется по проводам. Я предполагаю, что это потому, что вызов CreateSecurityTokenProvider () никогда не возвращал мой пользовательский поставщик токенов, так как SecurityTokenRequirement никогда не передается, указывая, что мой пользовательский токен необходим.

На стороне клиента у меня есть:

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

Эти методы в основном должны удалять учетные данные по умолчанию из конечной точки и присоединять новый экземпляр моего пользовательского CentralAuthCredentials. (Я взял это удаление / добавить комбо из статьи MSDN где-то).

В конфигурации:

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

Обратите внимание, что тип clientCredentials для поведения настроен на мои пользовательские учетные данные клиента. Тем не менее, на данный момент у меня есть привязка clientCredentialType со значением «UserName». Это поднимает мой 2-й вопрос : Какого черта должен clientCredentialType = "?" установить, если я использую пользовательские учетные данные? Согласно MSDN, доступны следующие значения безопасности Message : Нет , Windows , Имя пользователя , Сертификат , и IssuedToken .

Есть идеи? Надеюсь, я просто что-то упустил? Во всей реализации есть еще 6 классов, но я попытался включить только биты, необходимые для понимания ситуации ...


ОБНОВЛЕНИЕ № 1:

Я работал над этим весь день, и благодаря нескольким источникам я понял, что часть того, чего мне не хватало, была самым последним шагом на этой странице , который добавляет TokenParameters к привязка, так что привязка знает, как выглядит токен. Это ответ на мой первый 1-й вопрос; "Какого черта устанавливает требования к токену?" Ответ: TokenParameters присваивается привязке.

Итак, теперь я добавил следующее расширение, которое устанавливает TokenParameters для привязки:

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>

Ну, это делает меня на шаг дальше. Теперь я получаю новое исключение на сервере:

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

Похоже, что WCF использует некоторый менеджер токенов по умолчанию, чтобы попытаться обработать мой собственный токен, вместо моего собственного обработчика токена (конструктор моего собственного обработчика токена никогда не вызывается). Я думаю это происходит потому, что для клиента у меня есть этот конфиг:

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

Но на сервере У меня нет эквивалента, чтобы сообщить ему о пользовательских учетных данных клиента. Итак, новый вопрос : Где в конфиге для сервера я должен сообщить ему, что такое пользовательские ClientCredentials?


Обновление № 2:

Ну, я наконец-то разгадал немного больше загадки. Я только реализовал реализацию ClientCredentials, думая, что клиент отправляет кредиты, и это все. Клиент не аутентифицирует сервис, поэтому мне не нужны пользовательские ServiceCredentials. Ну, я был не прав. Указанный ServiceCredentials аутентифицирует токен из ClientCredentials и наоборот. Поэтому мне просто нужно было добавить собственную реализацию ServiceCredentials, которая передавала те же классы TokenSerializer и TokenAuthenticator.

Перейдем к следующей проблеме: WCF теперь игнорирует мои сертификаты x509, указанные в конфигурации, которые отлично работали с аутентификацией UserName. Я собираюсь открыть совершенно новый вопрос для этого!

1 Ответ

1 голос
/ 19 февраля 2009

Я столкнулся с похожими проблемами с приложением, над которым я работаю, к сожалению, я сдался, так как не мог заставить работать пользовательские учетные данные. Сейчас я использую имя пользователя / пароль (учетные данные клиента) и сертификат (учетные данные службы) с пользовательскими зашифрованными заголовками мыла, добавляемыми к служебным вызовам для передачи дополнительной информации, например, UserID и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...