-->

WebServiceTransportException: Unauthorized [401] i

2019-07-07 01:14发布

问题:

We are struggling to configure our web app to be able to connect with web services via Spring WS. We have tried to use the example from the documentation of client-side Spring-WS, but we end up with a WebServiceTransportException. The XML config looks like this:

<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
    <constructor-arg ref="messageFactory"/>
    <property name="messageSender">
        <bean class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
            <property name="credentials">
                <bean class="org.apache.commons.httpclient.UsernamePasswordCredentials">
                    <constructor-arg value="john"/>
                    <constructor-arg value="secret"/>
                </bean>
            </property>
        </bean>
    </property>
</bean>

We have been able to configure the application programmatically, but this configuration was not possible to "transfer" to a Spring XML config because some setters did not use the format Spring expects. (HttpState.setCredentials(...) takes two parameters). The config was lifted from some other Spring-WS client code in the company.

This is the configuration that works:

 public List<String> getAll() {
    List<String> carTypes = new ArrayList<String>();

    try {
        Source source = new ResourceSource(request);
        JDOMResult result = new JDOMResult();


        SaajSoapMessageFactory soapMessageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());

        WebServiceTemplate template = new WebServiceTemplate(soapMessageFactory);

        HttpClientParams clientParams = new HttpClientParams();
        clientParams.setSoTimeout(60000);
        clientParams.setConnectionManagerTimeout(60000);
        clientParams.setAuthenticationPreemptive(true);

        HttpClient client = new HttpClient(clientParams);
        client.getState().setCredentials(AuthScope.ANY,
                new UsernamePasswordCredentials("username", "password"));

        CommonsHttpMessageSender messageSender = new CommonsHttpMessageSender(client);

        template.setMessageSender(messageSender);
        template.sendSourceAndReceiveToResult(SERVICE_URI,
                source, result);

        // Handle the XML

    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (SOAPException e) {
        throw new RuntimeException(e);
    }

    return carTypes;
}

Does anyone know how to solve my problem? Every tutorial I have see out there lists the first configuration. It seems that when I set the credentials on the messageSender object, they are just ignored...

回答1:

Override HttpClient with a constructor that takes the parameters and wire through Spring using constructor-args

public MyHttpClient(HttpClientParams params, UsernamePasswordCredentials usernamePasswordCredentials) {
        super(params);        
        getState().setCredentials(AuthScope.ANY, usernamePasswordCredentials);
    }


回答2:

How do you distinguish these:

<constructor-arg value="john"/>
<constructor-arg value="secret"/>

try and replace it with this:

<property name="userName" value="john" />
<property name="password" value="secret" />

Hope it helps.



回答3:

If you are using a defaultHttpClient like you are in your example, Use the afterPropertiesSet method on your HTTPMessageSender and that should fix your problem by applying the credentials correctly



回答4:

At first we were setting credentials in our project like this:

<bean id="authenticationEnabledCommonsHttpMessageSender" parent="commonsHttpMessageSender"
    p:credentials-ref="clientCredentials" lazy-init="true" />
<bean id="clientCredentials"
    class="org.apache.commons.httpclient.UsernamePasswordCredentials"
    c:userName="${clientCredentials.userName}"
    c:password="${clientCredentials.password}"
    lazy-init="true" />

This is our cridentials enabled option. A problem occured while we are setting credentials like that. If the server we send message (has Axis impl) has not got username password credentials we get "Unauthorized" exception. Because ,when we trace vie TCPMon, we realized "username:password:" string was sent, as you can see username and password have no value.

After that we set the credentials like that:

public Message sendRequest(OutgoingRequest message, MessageHeaders headers,
                        EndpointInfoProvider endpointInfoProvider,
                        WebServiceMessageCallback requestCallback){
    Assert.notNull(endpointInfoProvider, "Destination provider is required!");
    final Credentials credentials = endpointInfoProvider.getCredentials();
    URI destinationUri = endpointInfoProvider.getDestination();
    for (WebServiceMessageSender messageSender : webServiceTemplate.getMessageSenders()) {
        if (messageSender instanceof CommonsHttpMessageSender) {
            HttpClient httpClient = ((CommonsHttpMessageSender) messageSender).getHttpClient();
            httpClient.getState().setCredentials(
                    new AuthScope(destinationUri.getHost(),
                            destinationUri.getPort(), AuthScope.ANY_REALM,
                            AuthScope.ANY_SCHEME), credentials
            );
            httpClient.getParams().setAuthenticationPreemptive(true);
            ((CommonsHttpMessageSender) messageSender)
                    .setConnectionTimeout(endpointInfoProvider
                            .getTimeOutDuration());
        }
    }

And the getCredentials methos is:

@Override
public Credentials getCredentials(){
    if (credentials != null) {
        return credentials;
    }
    String username = parameterService.usernameFor(getServiceName());
    String password = parameterService.passwordFor(getServiceName());
    if (username == null && password == null) {
        return null;
    }
    credentials = new UsernamePasswordCredentials(username, password);
    return credentials;
}