Java Web Service client basic authentication
Asked Answered
P

10

56

I have created a JAX-WS Web Service on top of Glassfish which requires basic HTTP authentication.

Now I want to create a standalone java application client for that Web Service but I don't have a clue of how to pass the username and password.

It works with Eclipse's Web Service explorer, and examining the wire I found this:

POST /SnaProvisioning/SnaProvisioningV1_0 HTTP/1.1
Host: localhost:8080
Content-Type: text/xml; charset=utf-8
Content-Length: 311
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: IBM Web Services Explorer
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Authorization: Basic Z2VybWFuOmdlcm1hbg==
Connection: close

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0="http://ngin.ericsson.com/sna/types/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <q0:listServiceScripts/>
  </soapenv:Body>
</soapenv:Envelope>

How do I pass the username and password in this "Authorization" header using java code? Is it hashed or something like that? What is the algorithm?

Without security involved I have a working standalone java client:

SnaProvisioning myPort = new SnaProvisioning_Service().getSnaProvisioningV10Port();
myPort.listServiceScripts();
Paba answered 15/8, 2011 at 22:5 Comment(0)
P
51

It turned out that there's a simple, standard way to achieve what I wanted:

import java.net.Authenticator;
import java.net.PasswordAuthentication;

Authenticator myAuth = new Authenticator() 
{
    @Override
    protected PasswordAuthentication getPasswordAuthentication()
    {
        return new PasswordAuthentication("german", "german".toCharArray());
    }
};

Authenticator.setDefault(myAuth);

No custom "sun" classes or external dependencies, and no manually encode anything.

I'm aware that BASIC security is not, well, secure, but we are also using HTTPS.

Paba answered 17/8, 2011 at 0:14 Comment(7)
Please note that Authenticator#setDefault is not thread safe, and this is not a good solution if your service consumer is hitting multiple endpoints.Sletten
Is there any thread safe alternative that allows multiple endpoints?Mendive
Authenticator is usefull for any kind of security protocol, but the JAX-WS way to provide the credentials do not require this method.Mendive
Unfortunately this solution will not work if your username in unicode and not from Latin1 :(Vicarial
If you want to programmatically change the credentials later, you may face this problem related to this bug/deficiency. If only the service port is protected with basic authentication, the easiest solution is (once again) Jonathan's answer. If the WSDL itself is protected, there are only the ugly workarounds described in the other thread.Throstle
@HeinBlöd if the WSDL is protected how can I access the service? Authenticator is not workingPruritus
@Pruritus I'm afraid I can't help you anymore, I've been out of touch with this subject ever since. But I'd assume that this accepted answer should work, or one of the others on that thread (?).Throstle
M
91

The JAX-WS way for basic authentication is

Service s = new Service();
Port port = s.getPort();

BindingProvider prov = (BindingProvider)port;
prov.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "myusername");
prov.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "mypassword");

port.call();
Mendive answered 3/9, 2012 at 17:57 Comment(3)
Thanks from me too! Would have taken me a long time to figure this out.Inchoate
for those who are 1.5 level, you will need to add this dependency for BindingProvider: <!-- mvnrepository.com/artifact/javax.xml/webservices-api --> <dependency> <groupId>javax.xml</groupId> <artifactId>webservices-api</artifactId> <version>1.5</version> </dependency>Crites
Great answer. Thank you very much.Bidet
P
51

It turned out that there's a simple, standard way to achieve what I wanted:

import java.net.Authenticator;
import java.net.PasswordAuthentication;

Authenticator myAuth = new Authenticator() 
{
    @Override
    protected PasswordAuthentication getPasswordAuthentication()
    {
        return new PasswordAuthentication("german", "german".toCharArray());
    }
};

Authenticator.setDefault(myAuth);

No custom "sun" classes or external dependencies, and no manually encode anything.

I'm aware that BASIC security is not, well, secure, but we are also using HTTPS.

Paba answered 17/8, 2011 at 0:14 Comment(7)
Please note that Authenticator#setDefault is not thread safe, and this is not a good solution if your service consumer is hitting multiple endpoints.Sletten
Is there any thread safe alternative that allows multiple endpoints?Mendive
Authenticator is usefull for any kind of security protocol, but the JAX-WS way to provide the credentials do not require this method.Mendive
Unfortunately this solution will not work if your username in unicode and not from Latin1 :(Vicarial
If you want to programmatically change the credentials later, you may face this problem related to this bug/deficiency. If only the service port is protected with basic authentication, the easiest solution is (once again) Jonathan's answer. If the WSDL itself is protected, there are only the ugly workarounds described in the other thread.Throstle
@HeinBlöd if the WSDL is protected how can I access the service? Authenticator is not workingPruritus
@Pruritus I'm afraid I can't help you anymore, I've been out of touch with this subject ever since. But I'd assume that this accepted answer should work, or one of the others on that thread (?).Throstle
S
10

for Axis2 client this may be helpful

...
serviceStub = new TestBeanServiceStub("<WEB SERVICE URL>"); // Set your value
HttpTransportProperties.Authenticator basicAuthenticator = new HttpTransportProperties.Authenticator();
List<String> authSchemes = new ArrayList<String>();
authSchemes.add(Authenticator.BASIC);
basicAuthenticator.setAuthSchemes(authSchemes); 
basicAuthenticator.setUsername("<UserName>"); // Set your value
basicAuthenticator.setPassword("<Password>"); // Set your value
basicAuthenticator.setPreemptiveAuthentication(true);
serviceStub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, basicAuthenticator);
serviceStub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, "false");
...
Scuffle answered 14/2, 2012 at 13:8 Comment(2)
I had to change the line authSchemes.add(Authenticator.BASIC); to authSchemes.add(HttpTransportProperties.Authenticator.BASIC); to get it to compile.Kries
Hi Avil, My Axis2 client does not have a Stub class present in it. Sorry, I'm a beginner. I'm consuming a commercial vendor's service and the client is provided by the vendor. I did wsimport, but there's no stub classes present. And so I can't _getServiceClient().getOptions() do this part. Do I need to add any additional attributes while doing wsimport? Please help me. Appreciate your help.Rampart
C
8

Some context additional about basic authentication, it consists in a header which contains the key/value pair:

Authorization: Basic Z2VybWFuOmdlcm1hbg==

where "Authorization" is the headers key, and the headers value has a string ( "Basic" word plus blank space) concatenated to "Z2VybWFuOmdlcm1hbg==", which are the user and password in base 64 joint by double dot

String name = "username";
String password = "secret";
String authString = name + ":" + password;
String authStringEnc = new BASE64Encoder().encode(authString.getBytes());
...
objectXXX.header("Authorization", "Basic " + authStringEnc);
Cardamom answered 11/7, 2017 at 22:58 Comment(0)
E
6

If you are using a JAX-WS implementation for your client, such as Metro Web Services, the following code shows how to pass username and password in the HTTP headers:

 MyService port = new MyService();
 MyServiceWS service = port.getMyServicePort();

 Map<String, List<String>> credentials = new HashMap<String,List<String>>();

 credentials.put("username", Collections.singletonList("username"));
 credentials.put("password", Collections.singletonList("password"));

 ((BindingProvider)service).getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, credentials);

Then subsequent calls to the service will be authenticated. Beware that the password is only encoded using Base64, so I encourage you to use other additional mechanism like client certificates to increase security.

Emersen answered 16/8, 2011 at 19:54 Comment(1)
This can be useful to manually add arbitrary headers and set arbitrary values (by manually calculating the base64 encoding of the credentials), but there are more standard optionsPaba
P
5

This worked for me:

 BindingProvider bp = (BindingProvider) port;
 Map<String, Object> map = bp.getRequestContext();
 map.put(BindingProvider.USERNAME_PROPERTY, "aspbbo");
 map.put(BindingProvider.PASSWORD_PROPERTY, "9FFFN6P");
Platter answered 28/5, 2017 at 0:3 Comment(0)
A
1

To make your life simpler, you may want to consider using JAX-WS framework such as Apache CXF or Apache Axis2.

Here is the link that describes how to setup WS-Security for Apache CXF -> http://cxf.apache.org/docs/ws-security.html

EDIT By the way, the Authorization field just uses simple Base64 encoding. According to this ( http://www.motobit.com/util/base64-decoder-encoder.asp ), the decoded value is german:german.

Aday answered 15/8, 2011 at 22:12 Comment(3)
The Web Service has been working for quite some time and adding security was just a matter of adding a single annotation (@RolesAllowed) to a single class, I don't think there would be something simpler than that, specially when I look at tons and tons of configurations needed by CXF. Besides that, changing frameworks just because the client can't send (yet) one header is not worth it.Paba
@German. Was not clear from your question that you were using any framework in your client. Looks like your own answer is a winner.Aday
Yeah, maybe it was not clear the framework that I use, and the thing is that we don't have any jar or dependency or anything referring a specific implementation, but I suppose that if we are using Glassfish, then we are using Metro.Paba
F
0

If you use JAX-WS, the following works for me:

    //Get Web service Port
    WSTestService wsService = new WSTestService();
    WSTest wsPort = wsService.getWSTestPort();

    // Add username and password for Basic Authentication
    Map<String, Object> reqContext = ((BindingProvider) 
         wsPort).getRequestContext();
    reqContext.put(BindingProvider.USERNAME_PROPERTY, "username");
        reqContext.put(BindingProvider.PASSWORD_PROPERTY, "password");
Frontiersman answered 22/8, 2019 at 23:48 Comment(0)
P
0

The easiest option to get this working is to include Username and Password under of request. See sample below.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:typ="http://xml.demo.com/types" xmlns:ser="http://xml.demo.com/location/services"
    xmlns:typ1="http://xml.demo.com/location/types">
    <soapenv:Header>
        <typ:requestHeader>
            <typ:timestamp>?</typ:timestamp>
            <typ:sourceSystemId>TEST</typ:sourceSystemId>
            <!--Optional: -->
            <typ:sourceSystemUserId>1</typ:sourceSystemUserId>
            <typ:sourceServerId>1</typ:sourceServerId>
            <typ:trackingId>HYD-12345</typ:trackingId>
        </typ:requestHeader>

        <wsse:Security soapenv:mustUnderstand="1"
            xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <wsse:UsernameToken wsu:Id="UsernameToken-emmprepaid"
                xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
                <wsse:Username>your-username</wsse:Username>
                <wsse:Password
                    Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">your-password</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <ser:getLocation>
            <!--Optional: -->
            <ser:GetLocation>
                <typ1:locationID>HYD-GoldenTulipsEstates</typ1:locationID>
            </ser:GetLocation>
        </ser:getLocation>
    </soapenv:Body>
</soapenv:Envelope>
Perseverance answered 7/11, 2019 at 11:12 Comment(0)
A
0

For me below code works

        Map<String, Object> requestContext = ((BindingProvider)port).getRequestContext();
        Map<String, List<String>> requestHeaders = new HashMap<String, List<String>>();
        String authString = "<Userid>" + ":" + "<Pwd>";
        String authorization =  Base64.getEncoder().encodeToString(authString.getBytes());
        requestHeaders.put("Authorization", Collections.singletonList("Basic " + authorization));
        requestContext.put(MessageContext.HTTP_REQUEST_HEADERS, requestHeaders);
Alleenallegation answered 31/1, 2023 at 14:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.