SOAP服务307重定向丢失授权头及契约冲突问题解决方案
问题场景
我通过Visual Studio中“Add Service Reference...”向导自动生成的客户端调用SOAP Web服务,初始web.config中的wsHttpBinding配置如下:
<wsHttpBinding> <binding name="ServiceBinding" allowCookies="true" receiveTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="52428800"> <readerQuotas maxDepth="52428800" maxStringContentLength="52428800" maxArrayLength="52428800" maxBytesPerRead="52428800" maxNameTableCharCount="52428800" /> <security mode="TransportWithMessageCredential"> <message clientCredentialType="UserName" /> </security> </binding> </wsHttpBinding>
调用时传入授权头,源服务使用HTTPS协议,初始状态下所有功能运行正常。
后续需求为不修改客户端代码前提下,实现请求到另一站点的重定向。最初方案是在源服务端返回307状态码,修改HttpContext.Current.Response.RedirectLocation配置跳转,但跳转后发往目标站点的请求未携带授权头,返回401未授权错误。
尝试将目标站点部署为HTTP协议、关闭授权校验,对应修改wsHttpBinding配置为:
<wsHttpBinding> <binding name="ServiceBinding" allowCookies="true" receiveTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="52428800"> <readerQuotas maxDepth="52428800" maxStringContentLength="52428800" maxArrayLength="52428800" maxBytesPerRead="52428800" maxNameTableCharCount="52428800" /> <security mode="None"> </security> </binding> </wsHttpBinding>
该配置下调用返回违反服务契约错误。当前约束:源站点必须使用HTTPS协议,目标站点可支持HTTP/HTTPS协议。
解决方案
两个报错的核心原因都是WCF客户端默认安全机制的限制,无需修改客户端业务代码,按以下方式调整即可实现需求:
问题根因说明
- 307跳转丢失授权头:WCF在
TransportWithMessageCredential安全模式下默认遵循同源安全策略,跨域HTTPS跳转、HTTPS转HTTP跳转时会自动剥离消息级UserName凭证和自定义授权头,属于框架层面的默认安全行为,无法仅通过服务端返回307状态码绕过。 - 改
security mode="None"报契约错误:修改客户端绑定安全模式为None后,客户端发出的SOAP消息不再携带WS-Security相关安全头,源站接收请求时会先校验安全头,消息结构不匹配直接触发契约校验失败,和目标站点配置无关。
可选实现方案
方案一:源站做反向代理转发(兼容性最好,零客户端改动)
放弃HTTP 307跳转逻辑,在源站服务端收到请求后,直接在服务端代码内发起对目标站点的请求,把客户端传入的凭证、请求报文完整转发到目标地址,拿到目标站返回结果后原样返回给客户端。
- 优势:完全不需要修改客户端任何配置、不需要调整现有web.config,客户端全程只和源站HTTPS端点通信,不存在跨域丢凭证、契约不匹配问题
- 注意事项:目标站如果使用HTTPS,内部自签证书场景可在源站转发逻辑中配置跳过证书校验;如果目标站用HTTP直接转发即可。
方案二:同根域HTTPS跳转(仅改web.config,不改业务代码)
如果必须使用跳转逻辑:
- 给目标站点部署HTTPS证书,保证和源站属于同一个根域名(例如源站为
api.example.com,目标站为api2.example.com,不可跨顶级域名) - 保留原有
TransportWithMessageCredential安全模式,仅修改客户端web.config的binding配置,新增允许重定向透传凭证的相关属性,配置参考如下:
<wsHttpBinding> <binding name="ServiceBinding" allowCookies="true" receiveTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="52428800" enableUnsecuredResponse="true"> <readerQuotas maxDepth="52428800" maxStringContentLength="52428800" maxArrayLength="52428800" maxBytesPerRead="52428800" maxNameTableCharCount="52428800" /> <security mode="TransportWithMessageCredential"> <message clientCredentialType="UserName" establishSecurityContext="false" negotiateServiceCredential="false"/> </security> </binding> </wsHttpBinding>
- 源站返回307跳转时,跳转地址必须为HTTPS协议,禁止跳转到HTTP地址,否则WCF会判定为不安全跳转自动剥离凭证。
注意:该方案仅支持同根域名下HTTPS到HTTPS的跳转,跨顶级域名场景下WCF依然会拦截凭证透传。
内容的提问来源于stack exchange,提问作者Davide Bono
相关产品推荐
相关产品推荐

