使用自定义 ClientCredentials 进行 WCF 身份验证:要使用的 clientCredentialType 是啥?

Posted

技术标签:

【中文标题】使用自定义 ClientCredentials 进行 WCF 身份验证:要使用的 clientCredentialType 是啥?【英文标题】:WCF Authentication with custom ClientCredentials: What is the clientCredentialType to use?使用自定义 ClientCredentials 进行 WCF 身份验证:要使用的 clientCredentialType 是什么? 【发布时间】:2010-10-08 11:04:57 【问题描述】:

我不得不放弃基本的 WCF 用户名/密码安全性并实现我自己的自定义客户端凭据,以保存超出默认提供的更多信息。

我通过this MSDN article 工作,但我错过了一些东西,因为它不起作用。

首先,我有一些提供自定义 ClientCredentialsSecurityTokenManager 的自定义 ClientCredentials:

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 不是我的自定义令牌。这提出了我的第一个问题: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 文章中抓取了这个 Remove/Add 组合)。

在配置中:

    <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”。这提出了我的第二个问题:clientCredentialType="到底应该是什么?"如果我使用自定义凭据,设置为?根据 MSDN,Message 安全性的可用值是:NoneWindowsUserNameCertificate IssuedToken

有什么想法吗?希望我只是缺少一些简单的东西?整个实现还有 6 个类,但我试图只包含了解情况所需的部分......


更新 #1:

我整天都在研究这个问题,感谢一些消息来源,我意识到我缺少的部分是this page 的最后一步,即将 TokenParameters 添加到绑定中,这样绑定知道令牌的样子。这就是我最初的第一个问题的答案; “到底是什么设置了令牌要求?”答案:分配给绑定的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 证书,这些证书在用户名身份验证中运行良好。我要为这个问题打开一个全新的问题!

【问题讨论】:

好的,所以这个问题读起来几乎和我前两天的一样! 【参考方案1】:

我正在使用的应用程序遇到了类似的问题,很遗憾我放弃了,因为我无法让自定义凭据正常工作。我现在使用用户名/密码(客户端凭据)和证书(服务凭据),并在服务调用中添加了自定义加密的soap标头以传递其他信息,例如用户ID等。

【讨论】:

以上是关于使用自定义 ClientCredentials 进行 WCF 身份验证:要使用的 clientCredentialType 是啥?的主要内容,如果未能解决你的问题,请参考以下文章

IdentityServer4 自定义授权模式

无法将 @LoadBalanced 与在 ClientCredentials 上配置的 OAuth2RestTemplate 一起使用

在 App.config 中设置 WCF ClientCredentials

如何使用 ClientCredentials grantType 向 IdentityServer4 生成的我的访问令牌添加声明

带有 ClientCredentials 流的 OpenIdDict 降级模式

CSS自定义下划线样式问题(高手进)