Consuming SOAP WS returns Error 401 unauthorized

2019-08-27 10:44发布

问题:

I am trying to consume a SOAP webservice using spring-ws. I am able to successfully consume this web service from SOAP UI. However I receive an unauthorized exception:

org.springframework.ws.client.WebServiceTransportException: Unauthorized [401]
    at org.springframework.ws.client.core.WebServiceTemplate.handleError(WebServiceTemplate.java:699)

How do I successfully authenticate?

My code is as follows:

WebServiceTemplate configuration:

@Bean
    public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller){
        WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
        webServiceTemplate.setDefaultUri("http://ultron.illovo.net:9704/AdminService");
        webServiceTemplate.setMarshaller(marshaller);
        webServiceTemplate.setUnmarshaller(marshaller);

        Credentials credentials = new UsernamePasswordCredentials("biqa", "welcome1");

        HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender();
        messageSender.setCredentials(credentials);
        messageSender.setReadTimeout(5000);
        messageSender.setConnectionTimeout(5000);
        webServiceTemplate.setMessageSender(messageSender);

        return webServiceTemplate;
    }

Client:

@Autowired

WebServiceTemplate webServiceTemplate;

public CallProcedureWithResultsResponse callProcedureWithResults(String procedureName){
    CallProcedureWithResults request = new CallProcedureWithResults();
    request.setProcedureName(procedureName);
    log.info("Calling procedure " + procedureName);

    CallProcedureWithResultsResponse response = (CallProcedureWithResultsResponse) webServiceTemplate.marshalSendAndReceive("http://ultron.illovo.net:9704/AdminService/AdminService", request);

    return response;
}

JUnit Test

@Test
    public void testWebService(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ObieeConfiguration.class);
        ObieeClient client = context.getBean(ObieeClient.class);
        CallProcedureWithResultsResponse response = client.callProcedureWithResults("GetOBISVersion()");
        client.printResponse(response);
        context.close();
    }

回答1:

I managed to resolve this issue by doing the following:

Create a subclass of HttpUrlConnectionMessageSender as follows:

public class WebServiceMessageSenderWithAuth extends HttpUrlConnectionMessageSender {

    String user;
    String password;

    public WebServiceMessageSenderWithAuth(String user, String password) {
        this.user = user;
        this.password = password;
    }

    @Override
     protected void prepareConnection(HttpURLConnection connection) throws IOException { 
        BASE64Encoder enc = new BASE64Encoder(); 
        String userpassword = user+":"+password;
        String encodedAuthorization = enc.encode( userpassword.getBytes() ); 
        connection.setRequestProperty("Authorization", "Basic " + encodedAuthorization); 
        super.prepareConnection(connection); 
    } 

}

My client is defines as follows:

public class ObieeClient extends WebServiceGatewaySupport {

    private static final Logger log = LoggerFactory.getLogger(ObieeClient.class);

    public CallProcedureWithResultsResponse callProcedureWithResults(String procedureName, String webServiceURL){
        CallProcedureWithResults request = new CallProcedureWithResults();
        request.setProcedureName(procedureName);
        log.info("Calling procedure " + procedureName);

        CallProcedureWithResultsResponse response = (CallProcedureWithResultsResponse) ((JAXBElement) getWebServiceTemplate().marshalSendAndReceive(webServiceURL, request)).getValue();

        return response;
    }
}

And finally the client is configured to use the credentials by setting the message sender:

     Jaxb2Marshaller marshaller = new Jaxb2Marshaller();            marshaller.setContextPath("za.co.adaptit.smartdeployment.webservices.wsdl");

                //set up web service client
                ObieeClient client = new ObieeClient();
                client.setDefaultUri(host);
                client.setMarshaller(marshaller);
                client.setUnmarshaller(marshaller);
                client.setMessageSender(new WebServiceMessageSenderWithAuth("user", "password"));

response = client.callProcedureWithResults("NQSModifyMetadata('" + obieeObjectsXML +"')", server.getHost());

Hope this helps.



回答2:

I run into the same issue and managed to resolved by defining a MessageSender with Credentials and then setting it to the client. I didn't have to create a custom MessageSender.

@Bean
public WebServiceMessageSender messageSender(Credentials credentials) throws Exception{
    HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender();
    messageSender.setCredentials(credentials);
    return messageSender;
}

@Bean
public Credentials credentials(){
    UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(USERNAME, PASSWORD);
    return credentials;

}


回答3:

use HttpsUrlConnectionMessageSender for secure Https connections. This is part of spring-ws-support dependency. Also, you can bypass various certificate issue by using HttpsUrlConnectonMessageSender, like certificate common name mis-matching.

Dont use BASE64Encoder, it is the class of sun.misc jar which is not much secure. use Base64 of java util present in java 1.8 and Base64 of apache for java version less then java 1.8

public class WebServiceMessageSenderWithAuth extends HttpsUrlConnectionMessageSender {

String user;
String password;

public WebServiceMessageSenderWithAuth(String user, String password) {
    this.user = user;
    this.password = password;
   }

@Override
 protected void prepareConnection(HttpURLConnection connection) throws IOException { 

    String userpassword = user+":"+password;
    String encodedAuthorization = Base64.encode( userpassword.getBytes() ); 
    connection.setRequestProperty("Authorization", "Basic " + encodedAuthorization); 
    //set various properties by using reference of connection according to your requirement
   } 
}