SecurityNegotiationException - 从 Web 服务检索令牌

SecurityNegotiationException - Retrieving token from web service

我正在尝试从 WIF 3.5 转移到 WIF 4.5。但是,事实证明转换比我预期的要困难。问题会对应代码中的注释。

完整错误信息:

System.Web.Services.Protocols.SoapException: 'System.Web.Services.Protocols.SoapException: Authentication Failed --->

System.ServiceModel.Security.SecurityNegotiationException: Secure channel cannot be opened because security negotiation with the remote endpoint has failed. This may be due to absent or incorrectly specified EndpointIdentity in the EndpointAddress used to create the channel. Please verify the EndpointIdentity specified or implied by the EndpointAddress correctly identifies the remote endpoint.

Secure channel cannot be opened because security negotiation with the remote endpoint has failed. This may be due to absent or incorrectly specified EndpointIdentity in the EndpointAddress used to create the channel. Please verify the EndpointIdentity specified or implied by the EndpointAddress correctly identifies the remote endpoint.

#1。需要哪个 username/password 组合,哪个不需要?

#2。这是抛出 SecurityNegotiationException 的地方。我到底错过了什么?

所以,我离题太远了还是我缺少了一些简单的东西?我是否需要完全重写 WSTrustChannelFactory 的创建方式?

代码:

public string GetToken(string url, string domain, string realm, string username, string password)
{
    string rp = realm;
    string token = "";

    WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory
    (
        new WSHttpBinding(SecurityMode.TransportWithMessageCredential),
        new EndpointAddress(new Uri(url))
    );
        
    trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
    trustChannelFactory.Credentials.Windows.ClientCredential.Domain = domain;
    trustChannelFactory.Credentials.Windows.ClientCredential.UserName = username; // #1; not sure which pair is needed?
    trustChannelFactory.Credentials.Windows.ClientCredential.Password = password;

    trustChannelFactory.Credentials.UserName.Password = password;
    trustChannelFactory.Credentials.UserName.UserName = username;

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

    try
    {
        RequestSecurityToken rst = new RequestSecurityToken(RequestTypes.Issue, KeyTypes.Bearer);
        rst.AppliesTo = new EndpointReference(rp);
        rst.TokenType = SecurityTokenTypes.Saml;

        WSTrustChannel channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
        GenericXmlSecurityToken token = channel.Issue(rst) as GenericXmlSecurityToken; // #2; Exception thrown here
        token = token.TokenXml.OuterXml;
    }
    catch (SecurityNegotiationException e)
    {
        LogError("Authentication Failed", e);
    }
    catch (TimeoutException e)
    {
        LogError("Unable to authenticate", e);
    }
    catch (CommunicationException e)
    {
        LogError("Communication exception", e);
    }
    catch (Exception e)
    {
        LogError("Unknown exception", e);
    }
    return token;
}

您需要使用 SecurityTokenHandlerCollection

        public SecurityToken GetToken(string url, string realm, string username, string password)
        {
            string rp = realm;
        
            WS2007HttpBinding binding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential, false);

            binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
            binding.Security.Message.EstablishSecurityContext = false;

            EndpointAddress endpoint = new EndpointAddress(url);

            WSTrustChannelFactory factory = new WSTrustChannelFactory(binding, endpoint);
            factory.TrustVersion = TrustVersion.WSTrust13;

            factory.Credentials.UserName.UserName = username;
            factory.Credentials.UserName.Password = password;

            WSTrustChannel channel = (WSTrustChannel) factory.CreateChannel();

            RequestSecurityToken rst = new RequestSecurityToken
            {
                RequestType = RequestTypes.Issue,
                KeyType = KeyTypes.Bearer,
                AppliesTo = new EndpointReference(rp),              
                TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"
            };

            GenericXmlSecurityToken genericXmlSecurityToken = (GenericXmlSecurityToken) channel.Issue(rst, out RequestSecurityTokenResponse rstr);          

            SecurityTokenHandlerCollection tokenHandlers = new SecurityTokenHandlerCollection(
                new SecurityTokenHandler[]
                {
                    new SamlSecurityTokenHandler(), 
                    new Saml2SecurityTokenHandler()
                }
            );
            tokenHandlers.Configuration.AudienceRestriction = new AudienceRestriction();
            tokenHandlers.Configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(rp));

            TrustedIssuerNameRegistry trustedIssuerNameRegistry = new TrustedIssuerNameRegistry();
            tokenHandlers.Configuration.IssuerNameRegistry = trustedIssuerNameRegistry;

            SecurityToken token =
                tokenHandlers.ReadToken(
                    new XmlTextReader(new StringReader(genericXmlSecurityToken.TokenXml.OuterXml)));

            return token;
        }

        public class TrustedIssuerNameRegistry : IssuerNameRegistry
        {
            public override string GetIssuerName(SecurityToken securityToken)
            {
                return "Trusted Issuer";
            }
        }

我们决定暂时继续使用 WIF 3.5,并将对 WIF 4.5 进行整个重写,而不是尝试做一些不可能的事情。

变化太多,文档不足,无法将我们现有的代码从 WIF 3.4 升级到 WIF 4.5