You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Framework 4.8下如何自定义RequestSecurityToken的Issuer格式?

问题场景

我用以下C#代码创建RequestSecurityToken:

var request = new RequestSecurityToken
{
     TokenType = "some:urn",
     AppliesTo = new EndpointReference($"some:other:urn"),
     Issuer = new EndpointReference("https://some.domain.net"),
     RequestType = RequestTypes.Issue
};

生成的XML中Issuer标签结构如下:

<trust:Issuer>
    <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
        <wsa:Address>https://some.domain.net/</wsa:Address>
    </wsa:EndpointReference>
</trust:Issuer>

但目标SecurityTokenService(STS)期望Issuer标签仅包含wsa:Address标签(无需EndpointReference包装):

<trust:Issuer>
    <wsa:Address xmlns:wsa="http://www.w3.org/2005/08/addressing">
        https://some.domain.net/
    </wsa:Address>
</trust:Issuer>

我已经实现了IClientMessageInspector用于添加请求头,但不想修改请求体内容;尝试过重写自定义类的Issuer属性,但序列化消息失败。请问除了修改发送前的XML外,还有其他解决方法吗?应用基于.NET Framework 4.8。

可行解决方案

1. 自定义RequestSecurityToken派生类+自定义DataContract序列化

默认RequestSecurityToken的Issuer属性类型是EndpointReference,序列化时会自动套上<wsa:EndpointReference>节点。可以创建派生类,替换Issuer的序列化逻辑,直接输出wsa:Address:

[DataContract(Name = "RequestSecurityToken", Namespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512")]
public class CustomRequestSecurityToken : RequestSecurityToken
{
    // 隐藏基类Issuer属性,自定义序列化逻辑
    [DataMember(Name = "Issuer")]
    public new EndpointAddress IssuerAddress { get; set; }

    // 保留其他属性的原有序列化逻辑
    [DataMember(Name = "TokenType")]
    public new string TokenType
    {
        get => base.TokenType;
        set => base.TokenType = value;
    }

    [DataMember(Name = "AppliesTo")]
    public new EndpointReference AppliesTo
    {
        get => base.AppliesTo;
        set => base.AppliesTo = value;
    }

    [DataMember(Name = "RequestType")]
    public new string RequestType
    {
        get => base.RequestType;
        set => base.RequestType = value;
    }
}

创建请求时使用该自定义类,直接将IssuerAddress赋值为EndpointAddress:

var request = new CustomRequestSecurityToken
{
    TokenType = "some:urn",
    AppliesTo = new EndpointReference($"some:other:urn"),
    IssuerAddress = new EndpointAddress("https://some.domain.net"),
    RequestType = RequestTypes.Issue
};

此方式序列化后,Issuer节点会直接输出wsa:Address,不会被EndpointReference包裹。

2. 使用IContractResolver修改序列化逻辑

若不想创建派生类,可通过自定义IContractResolver拦截RequestSecurityToken的Issuer属性序列化,替换为直接输出Address节点:

public class IssuerContractResolver : DataContractResolver
{
    private readonly DataContractResolver _defaultResolver;

    public IssuerContractResolver(DataContractResolver defaultResolver)
    {
        _defaultResolver = defaultResolver;
    }

    public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        return _defaultResolver.TryResolveType(type, declaredType, knownTypeResolver, out typeName, out typeNamespace);
    }

    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        return _defaultResolver.ResolveName(typeName, typeNamespace, declaredType, knownTypeResolver);
    }

    public override object ResolveContract(object obj)
    {
        if (obj is EndpointReference epRef)
        {
            // 将EndpointReference替换为EndpointAddress,让序列化直接输出Address节点
            return epRef.Address;
        }
        return _defaultResolver.ResolveContract(obj);
    }
}

创建DataContractSerializer时传入该解析器:

var serializer = new DataContractSerializer(
    typeof(RequestSecurityToken), 
    null, 
    int.MaxValue, 
    false, 
    true, 
    null, 
    new IssuerContractResolver(DataContractSerializer.DefaultDataContractResolver)
);
// 使用该序列化器处理请求消息

3. 自定义ClientMessageFormatter控制消息序列化

实现IClientMessageFormatter,在序列化阶段直接调整Issuer节点结构,无需修改原始XML字符串:

public class CustomMessageFormatter : IClientMessageFormatter
{
    private readonly IClientMessageFormatter _originalFormatter;

    public CustomMessageFormatter(IClientMessageFormatter originalFormatter)
    {
        _originalFormatter = originalFormatter;
    }

    public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
    {
        var message = _originalFormatter.SerializeRequest(messageVersion, parameters);
        
        // 遍历消息内容,修改Issuer节点结构
        var bodyReader = message.GetReaderAtBodyContents();
        var xmlDoc = new XmlDocument();
        xmlDoc.Load(bodyReader);
        
        var manager = new XmlNamespaceManager(xmlDoc.NameTable);
        manager.AddNamespace("trust", "http://docs.oasis-open.org/ws-sx/ws-trust/200512");
        manager.AddNamespace("wsa", "http://www.w3.org/2005/08/addressing");
        
        var issuerNode = xmlDoc.SelectSingleNode("//trust:Issuer/wsa:EndpointReference", manager);
        if (issuerNode != null)
        {
            var addressNode = issuerNode.SelectSingleNode("wsa:Address", manager);
            if (addressNode != null)
            {
                issuerNode.ParentNode.ReplaceChild(addressNode, issuerNode);
            }
        }
        
        var newBody = XmlReader.Create(new StringReader(xmlDoc.InnerXml));
        return Message.CreateMessage(messageVersion, message.Headers.Action, newBody);
    }

    public object[] DeserializeReply(Message message, object[] parameters)
    {
        return _originalFormatter.DeserializeReply(message, parameters);
    }
}

通过自定义IEndpointBehavior将格式化器附加到客户端端点:

public class CustomEndpointBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        foreach (var operation in clientRuntime.Operations)
        {
            operation.Formatter = new CustomMessageFormatter(operation.Formatter);
        }
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }

    public void Validate(ServiceEndpoint endpoint) { }
}

最后在客户端添加该行为:

var client = new YourStsClient();
client.Endpoint.Behaviors.Add(new CustomEndpointBehavior());

以上方法均无需直接修改发送前的XML字符串,而是通过WCF的序列化扩展点调整输出结构,更符合WCF设计规范。

内容的提问来源于stack exchange,提问作者Francesco Franzini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 16:43:15