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

C#调用Workday人力资源Web服务V38.1认证失败问题咨询

异常原因

Workday的SOAP服务根本不认HTTP传输层的认证信息,你之前写的_proxy.Credentials = new NetworkCredential(...)是把用户名密码塞到HTTP请求的Authorization头里,Workday服务端根本不会读这个位置的凭据,它要求认证信息必须放在SOAP信封内部的WS-Security(UsernameToken)头里传递。
你在SOAP UI里能调用成功,是因为SOAP UI在你填了认证信息之后,自动帮你把符合规范的WS-Security头插到SOAP请求里了。而WSDL.exe生成的代理类默认没有自动生成、注入这个安全头的逻辑,服务端收到的请求没有有效认证凭据,自然直接返回用户名密码错误,哪怕你账号密码本身是对的。
另外还有个高频踩坑点:Workday的认证用户名不是单纯的账号名,必须带租户前缀,格式是租户ID\账号名,少了租户前缀也会报完全相同的错误。

修复方法
  • 不要修改WSDL.exe生成的原始cs文件,新建一个同命名空间的分部类,给HumanResourcesService扩展WS-Security头注入逻辑:
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Web.Services.Protocols;
using System.Xml;

public partial class HumanResourcesService
{
    private string _authUsername;
    private string _authPassword;

    /// <summary>
    /// 初始化Workday人力资源服务客户端
    /// </summary>
    /// <param name="username">集成账号名,不需要带租户前缀</param>
    /// <param name="password">账号密码</param>
    /// <param name="tenantId">Workday租户ID</param>
    /// <param name="endpointUrl">V38.1版本对应的服务端点地址</param>
    public HumanResourcesService(string username, string password, string tenantId, string endpointUrl)
    {
        _authUsername = $"{tenantId}\\{username}";
        _authPassword = password;
        this.Url = endpointUrl;
        this.SoapVersion = SoapProtocolVersion.Soap11;
        this.RequestEncoding = Encoding.UTF8;
        this.EnableDecompression = true;
    }

    protected override WebRequest GetWebRequest(Uri uri)
    {
        var req = (HttpWebRequest)base.GetWebRequest(uri);
        // Workday部分版本要求SOAPAction头为空,避免400错误
        req.Headers.Add("SOAPAction", string.Empty);
        return req;
    }

    protected override void WriteSoapMessage(SoapClientMessage message, Stream stream)
    {
        // 构造符合WS-Security 1.0规范的UsernameToken头,密码使用PasswordText格式(Workday强制要求HTTPS传输,不需要加密密码)
        const string wsseNs = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
        const string wsuNs = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
        const string soapEnvNs = "http://schemas.xmlsoap.org/soap/envelope/";
        const string pwdType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";

        var securityXml = new XmlDocument();
        var securityNode = securityXml.CreateElement("wsse", "Security", wsseNs);
        securityNode.SetAttribute("mustUnderstand", soapEnvNs, "1");

        var tokenNode = securityXml.CreateElement("wsse", "UsernameToken", wsseNs);
        tokenNode.SetAttribute("Id", wsuNs, "UsernameToken-" + Guid.NewGuid().ToString("N"));

        var usernameNode = securityXml.CreateElement("wsse", "Username", wsseNs);
        usernameNode.InnerText = _authUsername;
        tokenNode.AppendChild(usernameNode);

        var pwdNode = securityXml.CreateElement("wsse", "Password", wsseNs);
        pwdNode.SetAttribute("Type", pwdType);
        pwdNode.InnerText = _authPassword;
        tokenNode.AppendChild(pwdNode);

        securityNode.AppendChild(tokenNode);
        message.Headers.Add(new WorkdaySecurityHeader(securityNode));
        
        base.WriteSoapMessage(message, stream);
    }
}

/// <summary>
/// 自定义WS-Security请求头实现
/// </summary>
internal class WorkdaySecurityHeader : SoapHeader
{
    private readonly XmlElement _securityContent;

    public WorkdaySecurityHeader(XmlElement content)
    {
        _securityContent = content;
        MustUnderstand = true;
    }

    public override XmlElement GetXml(XmlDocument document)
    {
        return (XmlElement)document.ImportNode(_securityContent, true);
    }
}
  • 替换原来的代理初始化逻辑,删掉之前设置HTTP层Credentials的无效代码:
// 替换成你实际的租户ID、账号、密码、V38.1端点地址
_proxy = new HumanResourcesService(
    WorkdayHRWebservicesUsername, 
    WorkdayHRWebservicesPassword, 
    WorkdayTenantId,
    "https://你的租户域名.workday.com/ccx/service/你的租户ID/Human_Resources/v38.1"
);
// 不需要设置UseDefaultCredentials、Credentials、PreAuthenticate这些HTTP认证相关的属性,设置了也不生效
  • 调用前做以下校验,避免其他配置问题:
    • 确认使用的集成账号已经在Workday后台绑定了对应安全组,拥有Human_Resources服务下Get_Worker_Profile接口的访问权限
    • 确认端点地址和你SOAP UI里用的完全一致,不要混用其他版本(比如V40/V39)的服务地址
    • 确认租户ID拼写正确,用户名的租户前缀和租户ID完全匹配

内容的提问来源于stack exchange,提问作者Øyvind

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 23:51:24