验证 Spring SOAP 到 Workday

Posted

技术标签:

【中文标题】验证 Spring SOAP 到 Workday【英文标题】:Authentication Spring SOAP to Workday 【发布时间】:2016-11-11 07:07:08 【问题描述】:

我对处理 SOAP 请求非常陌生,我正在尝试使用列出的 Workday 的 SOAP api here。我使用了一个 gradle/ant 脚本来根据 Spring 教程 here 从 WSDL 生成类

现在,类已经生成。我可以访问我需要的功能。问题是我不知道如何验证我的请求。

这是我目前所拥有的:

import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import workday_Staffing.wsdl.GetWorkersRequestType;
import workday_Staffing.wsdl.GetWorkersResponseType;

public class StaffingClient extends WebServiceGatewaySupport 

    public StaffingClient() 
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setContextPath("workday_Staffing.wsdl");
        setMarshaller(jaxb2Marshaller);
        setUnmarshaller(jaxb2Marshaller);
    

    public void makeWorkdayRequest() 

        // make the request - missing some authentication here
        GetWorkersRequestType request = new GetWorkersRequestType();
        GetWorkersResponseType workersResponseType = (GetWorkersResponseType) getWebServiceTemplate()
            .marshalSendAndReceive(request);
    

here 的答案似乎是一个很好的线索,但我不确定如何构建客户端并添加身份验证。

任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

主要是,您似乎缺少通过 SOAPHandler 为 WS-Security Credentials 提供的身份验证部分。仅供参考,这不是 Spring,而是从 API v16 开始的工作代码。

感谢您的记忆之旅。

步骤

    设置 Workday 用户名和租户 设置工作日密码 设置 Web 服务客户端存根(例如 HumanResourcesService) 设置 Web Service 客户端端口 将 WorkdayCredentials 处理程序添加到客户端存根,并引用端口、工作日用户@租户和工作日密码。 获取请求上下文 设置请求上下文的工作日 Web 服务端点(请参阅 wdEndPoint) GetWorkersRequestType 可选择为您的请求设置一些参数 获取响应 获取响应数据

例程

GetWorkers WorkdayCredentials

GetWorkers 代码

package com.workday.demo;

import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.Map;
import java.math.BigDecimal;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.ws.BindingProvider;

import com.workday.human_resources.*;

public class GetWorkers 

    public static void main(String[] args) 

        try 

            // Enter user/password and endpoint information for Proof of Concept
            final String wdUser = "user@tenant";
            final String wdPassword = "zzz";

            // final String wdEndpoint =
            // "https://e2-impl-cci.workday.com/ccx/service/exampleTenant/Human_Resources/v16";
            final String wdEndpoint = "https://impl-cc.workday.com/ccx/service/exampleTenant/Human_Resources/v16";

            System.out.println("Starting...");

            // Create the Web Service client stub
            HumanResourcesService service = new HumanResourcesService();
            HumanResourcesPort port = service.getHumanResources();

            // Add the WorkdayCredentials handler to the client stub
            WorkdayCredentials.addWorkdayCredentials((BindingProvider) port, wdUser, wdPassword);

            // Assign the Endpoint URL
            Map<String, Object> requestContext = ((BindingProvider) port).getRequestContext();
            requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,   wdEndpoint);

            // Define the paging defaults
            final int countSize = 200;
            int totalPages = 1;
            int currentPage = 1;

            // Set the current date/time
            GregorianCalendar cal = new GregorianCalendar();
            XMLGregorianCalendar xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);

            // Loop over all of the pages in the web service response
            while (totalPages >= currentPage) 
                // Create a "request" object
                GetWorkersRequestType request = new GetWorkersRequestType();

                // Set the WWS version desired
                request.setVersion("v10");

                // Set the date/time & page parameters in the request
                ResponseFilterType responseFilter = new ResponseFilterType();
                responseFilter.setAsOfEntryDateTime(xmlCal);
                responseFilter.setAsOfEffectiveDate(xmlCal);
                responseFilter.setPage(BigDecimal.valueOf(currentPage));
                responseFilter.setCount(BigDecimal.valueOf(countSize));
                request.setResponseFilter(responseFilter);

                // Set the desired response group(s) to return
                WorkerResponseGroupType responseGroup = new WorkerResponseGroupType();
                responseGroup.setIncludeReference(true);
                request.setResponseGroup(responseGroup);

                // Submit the request creating the "response" object
                GetWorkersResponseType response = port.getWorkers(request);

                // Display all Workers
                Iterator<WorkerType> i = response.getResponseData().getWorker()
                        .iterator();
                while (i.hasNext()) 
                    WorkerType worker = i.next();
                    
                        System.out.println(worker.getWorkerReference()
                                .getDescriptor());
                    
                

                // Update page number
                if (totalPages == 1) 
                    totalPages = response.getResponseResults().getTotalPages()
                            .intValue();
                
                currentPage++;
            

         catch (ProcessingFaultMsg e) 
            e.printStackTrace();
         catch (ValidationFaultMsg e) 
            e.printStackTrace();
         catch (DatatypeConfigurationException e) 
            e.printStackTrace();
        
    

WorkdayCredentials 代码

package com.workday.demo;

import java.util.List;

import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.namespace.QName;
import java.util.Set;

/**
 * This class creates a handler that will add the WS-Security username and
 * password to the to the SOAP request messages for a client side proxy.
 * 
 */
public class WorkdayCredentials implements SOAPHandler<SOAPMessageContext> 

    /** Namespace for the SOAP Envelope. */
    private static String SOAPENVNamespace = "http://schemas.xmlsoap.org/soap/envelope/";

    /** The prefix that will be used for the SOAP Envelope namespace. */
    private static String SOAPENVPrefix = "soapenv";

    /** Namespace for the WS-Security SOAP header elements. */
    private static String WSSENamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

    /** The prefix that will be used for the WS-Security namespace. */
    private static String WSSEPrefix = "wsse";

    /**
     * The WS-Security URI that specifies that the password will be transmitted
     * as plain text.
     */
    private static String WSSEPasswordText = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";

    /**
     * The user name that will be sent in the WS-Security header on the SOAP
     * request message. This is of the form systemid@tenant.
     */
    private String username;

    /**
     * The password that will be sent in the WS-Security header on the SOAP
     * request message.
     */
    private String password;

    /**
     * This method created an instance of the WorkdayCredentials class and adds
     * it as a handler to the bindingProvider supplied.
     * 
     * @param bindingProvider
     *            The client stub to which the handler will be added. The most
     *            convenient way to obtain the required bindingProvvider is to
     *            call one of the getPort methods on the Service class for the
     *            Web service and then cast the returned object to a
     *            BindingProvider.
     * @param username
     *            The id and tenant name for the user. This is of the form
     *            systemid@tenant.
     * @param password
     *            The password for the system user.
     */
    @SuppressWarnings("unchecked")
    public static void addWorkdayCredentials(BindingProvider bindingProvider,
            String username, String password) 
        List<Handler> handlerChain = bindingProvider.getBinding().getHandlerChain();
        handlerChain.add(new WorkdayCredentials(username, password));
        bindingProvider.getBinding().setHandlerChain(handlerChain);
    

    /**
     * Creates a WorkdayCredentials handler and initialises the member
     * variables. In most cases, the addWorkdayCredentials static method should
     * be used instead.
     * 
     * @param username
     *            The id and tenant name for the user. This is of the form
     *            systemid@tenant.
     * @param password
     *            The password for the system user.
     */
    public WorkdayCredentials(String username, String password) 
        this.username = username;
        this.password = password;
    

    /**
     * Returns null as this handler doesn't process any Headers, it just adds
     * one.
     */
    public Set<QName> getHeaders() 
        return null;
    

    /**
     * Adds WS-Security header to request messages.
     */
    public boolean handleMessage(SOAPMessageContext smc) 
        Boolean outboundProperty = (Boolean) smc
                .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (outboundProperty.booleanValue()) 
            addWSSecurityHeader(smc, username, password);
        
        return true;
    

    /**
     * Returns true, no action is taken for faults messages.
     */
    public boolean handleFault(SOAPMessageContext smc) 
        return true;
    

    public void close(MessageContext messageContext) 
    

    /**
     * Adds a WS-Security header containing a UsernameToken to a SOAP message.
     * 
     * @param smc
     *            The SOAPMessageContent to which the WS-Security header will be
     *            added.
     * @param username
     *            The WS-Security username.
     * @param password
     *            The WS-Security password.
     * 
     * @throws java.lang.RuntimeException
     *             This exception will be thrown if a SOAPException occurs when
     *             modifying the message.
     */
    private void addWSSecurityHeader(SOAPMessageContext smc, String username,
            String password) throws java.lang.RuntimeException 

        try 
            // Get the SOAP Header
            SOAPMessage message = smc.getMessage();
            SOAPHeader header = message.getSOAPHeader();
            if (header == null) 
                // Create header as it doesn't already exist
                message.getSOAPPart().getEnvelope().addHeader();
                header = message.getSOAPHeader();
            

            // Add WS-Security SOAP Header
            SOAPElement heSecurity = header.addChildElement("Security",
                    WSSEPrefix, WSSENamespace);
            heSecurity.addAttribute(message.getSOAPPart().getEnvelope()
                    .createName("mustUnderstand", SOAPENVPrefix,
                            SOAPENVNamespace), "1");

            // Add the Usernametoken element to the WS-Security Header
            SOAPElement heUsernameToken = heSecurity.addChildElement(
                    "UsernameToken", WSSEPrefix, WSSENamespace);

            // Add the Username element to the UsernameToken Element
            heUsernameToken.addChildElement("Username", WSSEPrefix,
                    WSSENamespace).addTextNode(username);

            // Add the Password element to the UsernameToken Element
            SOAPElement hePassword = heUsernameToken.addChildElement(
                    "Password", WSSEPrefix, WSSENamespace);
            hePassword.addAttribute(message.getSOAPPart().getEnvelope()
                    .createName("Type"), WSSEPasswordText);
            hePassword.addTextNode(password);

         catch (SOAPException e) 
            throw new RuntimeException(
                    "Failed to add WS-Security header to request", e);
        
    

【讨论】:

您好,谢谢您的回复。您说您使用的不是最新的 api 的 v16。有没有更新的方法,或者我应该简单地更改 url 中的 API 版本?另外,我没有看到您使用Jaxb2Marshaller。为什么? 您当然应该使用更新版本的 API,例如 26.2。 Workday 通常会对其 API 进行版本化,并在一段时间内支持之前的版本。在开发新东西时,最好使用 API 的最新稳定版本。要使此示例代码正常工作,您需要运行 Axis2 wsdl2java 来生成与 v16、26.2 或当前版本匹配的新类。随意尝试对您的租户进行此操作。为什么不是 Jaxb2Marshaller?这是春天的一部分,我的例子不是。

以上是关于验证 Spring SOAP 到 Workday的主要内容,如果未能解决你的问题,请参考以下文章

使用SOAP编组的Spring 5 Web服务客户端问题

Spring Boot文档阅读笔记-3 Ways to Add Custom Header in Spring SOAP Request

Spring Boot文档阅读笔记-3 Ways to Add Custom Header in Spring SOAP Request

Soap从入门到实战

SOAP 错误 5001(对等方未通过身份验证)

通过 SOAP 服务向 JIRA 进行身份验证时安全密码存储