9

I have written a REST web service with Jersey Server (that totally rocks !). I am now developing the client part of it, with Jersey Client as well.

On the server side, I have chosen a DIGEST authentication, because I personally think that BASIC authentication is an heresy that should be marked as "DEPRECATED" in our heads.

Unfortunately, I do not see any support of the Digest authentication on the client side. For BASIC authentication, one does something like :

client.addFilter(
    new HTTPBasicAuthFilter(
        user, 
        password));

But I see no "HTTPDigestAuthFilter" counterpart. Am I missing something ?

Thanks for your help,

Raphael

skaffman
  • 398,947
  • 96
  • 818
  • 769
Raphael Jolivet
  • 3,940
  • 5
  • 36
  • 56
  • Ok, I've asked on the Jersey mailing list and it currently does not exist. So I am implementing it. I'll post it there as soon as I get it working. – Raphael Jolivet Jun 09 '10 at 09:20
  • Please follow the progress on the corresponding thread of Jersey mailing list, on Nabble: http://jersey.576304.n2.nabble.com/DIGEST-Authentication-with-Jersey-client-td5132921.html – Raphael Jolivet Jun 14 '10 at 10:00
  • Why should *HTTP basic access authentication* be deprecated? – user359996 Nov 04 '10 at 20:28
  • 1
    Because the passwords are not encrypted and any man-in-the-middle can see / record them. – Raphael Jolivet Nov 05 '10 at 16:44
  • 8
    Basic access authentication makes no claims to encrypt traffic, so this isn't a shortcoming. In fact, it's a nice separation of concerns--use basic access authentication to *authenticate*, and TLS to *encrypt*. One only needs the extra complexity of digest authentication if both 1) encrypted transport is not an option, and 2) the message payload need not be encrypted. If the latter is not the case, encrypted transport is necessary, and digest authentication just becomes an extra and unnecessary burden relative to basic access authentication's simplicity. Different tools for different needs. – user359996 Nov 07 '10 at 18:25
  • Thankyou for adding in the HTTPBasicAuthFilter example, it was just what I needed. Basic Auth is fine for my needs and I couldn't find an example anywhere! – Dick Chesterwood Mar 18 '11 at 21:48

2 Answers2

23

I have just implemented it. I have created a feature request in the Jersey issue tracker, and posted my implementation there, as attachment : https://jersey.dev.java.net/issues/show_bug.cgi?id=542

It works fine for communicating with a DIGEST authentication of a Tomcat server. I have not tested for other web servers yet.

Raphael Jolivet
  • 3,940
  • 5
  • 36
  • 56
0

Here I wrote some random uri. Please fill your desired URI

For sample testing you can take help of google services which are available in the internet for open.

    import javax.ws.rs.core.*;
    import org.apache.commons.codec.digest.*;
    import org.codehaus.jettison.json.*;
    import com.sun.jersey.api.*;


    public class DigestClient {

    //Dividing into two parts because we need to send the last part of uri in our second request to service.
    static String baseUri = "https://www.something.com";
    static String subUri = "/later-part";

    public static void main(String[] args) throws JSONException{

        ClientConfig cc = new DefaultClientConfig();
        Client client = Client.create(cc);

        WebResource webResource = client.resource(baseUri+subUri);
        ClientResponse response = webResource.get(ClientResponse.class);
        // Basically in Digest-Authentication mechanism, we hit the rest service two times. 
        // First time with No Authentication, which returns some values (qop, nonce, realm) which are used as inputs in second call to rest service.


        /*--------------- First call-----------------*/
        // We get 401, Unauthorized
        System.out.println(response.getStatus()+"   "+response.getStatusInfo());
        // Here is the complete header information
        System.out.println(response.getHeaders());
        // We need "WWW-Authenticate" part information for our second call to rest
        System.out.println("WWW-Authenticate: \t" + response.getHeaders().get("www-Authenticate"));


        String noAuthResp = response.getHeaders().get("www-Authenticate").toString();
        noAuthResp = noAuthResp.replace("Digest ", "");
        noAuthResp = noAuthResp.replace('[', '{');
        noAuthResp = noAuthResp.replace(']', '}');

        // Creating a JSONObject for easy information retrieval 
        JSONObject resp = new JSONObject(noAuthResp);


        /*--------------- Second call-----------------*/
        // Here client has to set the fields which was returned from the first call
        String user = "postman";          // username
        String password = "password";          // password
        String realm = resp.getString("realm");          // realm value from the first rest-call response
        String qop = resp.getString("qop");          //qop value from the first rest-call response
        String nonce = resp.getString("nonce");          // nonce value from the first rest-call response
        String opaque = resp.getString("opaque");          // Some times if we don't get this value, set it with ""
        String algorithm = "MD5";          // The algorithm set by the  client
        int nonceCount = 678;          // Some numerical input from the client
        String clientNonce = "afdjas0";          // Some random text from the client for encryption

        String method = "GET";          // HTTP method

        String ha1 = new DigestClient().formHA1(user, realm, password);
        String ha2 = new DigestClient().formHA2(method, subUri);
        String responseCode =  new DigestClient().generateResponse(ha1, nonce, nonceCount, clientNonce, qop, ha2);

        // Header to be sent to the service
        String value = "Digest username=\""+user+"\", realm=\""+realm+"\", nonce=\""+nonce+"\", uri=\""+subUri+"\", qop="+qop+", nc="+nonceCount+", cnonce=\""+clientNonce+"\", response=\""+responseCode+"\", opaque=\""+opaque+"\"";          

        // Hitting the service
        response = webResource.header("authorization", value).type(MediaType.TEXT_PLAIN).accept("*").get(ClientResponse.class);
        System.out.println("\nComplete Response:\n"+response+"\n");
        String output = response.getEntity(String.class);
        System.out.println("Response Text: "+output);
    }

    // For generating HA1 value
    public String formHA1(String userName,String realm,String password){
        String ha1 = DigestUtils.md5Hex(userName + ":" + realm + ":" + password);
        return ha1;
    }
    // For generating HA2 value
    public String formHA2(String method,String uri){
        String ha2=DigestUtils.md5Hex(method + ":" + uri);
        return ha2;
    }

    // For generating response at client side
    public String generateResponse(String ha1,String nonce,int nonceCount,String clientNonce,String qop,String ha2){
        String response=DigestUtils.md5Hex(ha1 + ":" + nonce + ":" + nonceCount + ":" +clientNonce +":" + qop + ":" +ha2);
        return response;

    }
    }