jersey
  1. jersey
  2. JERSEY-2061

Client can be forced to connect to a resource with a different set of credentials if previously accessed via HttpURLConnection

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Won't Fix
    • Affects Version/s: 1.17, 2.4.1
    • Fix Version/s: 2.6
    • Component/s: security
    • Labels:
      None
    • Environment:

      JDK 7u17, Linux

      Description

      The Java authenticator framework use by URL, HttpURLConnection and the like for user interface reasons will remember the credentials used to access a particular URI over the life of the VM until such time as this set of credentials return a 401 response.

      Unfortunately this can mean that the default handler for the Client will inherit this pre-cached information. In this example the expected output should be:

      username="authenticator",
      username="jersey",

      but instead when you run this code you see:

      username="authenticator",
      username="authenticator",

      Here is the code that exercises the bug, if you modify the code to use Basic rather than Digest then it will pass; but that is because of JERSEY-2050.

      package authenticatorissue;
      
      import com.sun.jersey.api.client.Client;
      import com.sun.jersey.api.client.WebResource;
      import com.sun.jersey.api.client.filter.HTTPDigestAuthFilter;
      import com.sun.jersey.api.container.httpserver.HttpServerFactory;
      import com.sun.jersey.api.core.ClassNamesResourceConfig;
      import com.sun.net.httpserver.HttpServer;
      
      import java.io.DataInputStream;
      
      import java.net.Authenticator;
      import java.net.PasswordAuthentication;
      import java.net.URL;
      
      import javax.ws.rs.GET;
      import javax.ws.rs.HeaderParam;
      import javax.ws.rs.Path;
      import javax.ws.rs.core.Response;
      
      public class VerifyIssue {
          public static void main(String[] args) throws Exception {
      
              // Publish the REST endpoint
      
              HttpServer server =
                  HttpServerFactory.create("http://localhost:8080/rest",
                                           new ClassNamesResourceConfig(GetResourceBasic.class));
      
              try {
                  server.start();
      
                  // Replace the VM default authenticator with one that just returns authenticator/authenticator
      
                  Authenticator.setDefault(new Authenticator() {
      
                      @Override
                      protected PasswordAuthentication getPasswordAuthentication() {
                          return new PasswordAuthentication("authenticator", "authenticator".toCharArray());
                      }
                  });
      
                  // Get that resource using just URL
      
                  URL resource = new URL("http://localhost:8080/rest/get-basic");
                  try (DataInputStream dis = new DataInputStream(resource.openStream())) {
      
                      byte buffer[] = new byte[1024];
                      int length = dis.read(buffer);
                      System.out.println(new String(buffer, 0, length));
      
                  }
      
                  // Now get that resourece using a Jersey client
      
                  Client client = Client.create();
                  client.addFilter(new HTTPDigestAuthFilter("jersey", "jersey"));
      
                  WebResource webResource = client.resource(resource.toURI());
      
                  System.out.println(webResource.get(String.class));
      
      
                  //
      
              } finally {
                  server.stop(0);
      
              }
      
          }
      
      
          @Path("/get-basic")
          public static class GetResourceBasic {
      
              @GET
              public Response get(@HeaderParam("authorization") String authenticate) {
      
                  if (authenticate != null) {
                      // Dump out the user name
                      return Response.ok(authenticate.split(" ")[1].split(", ")[0]).build();
      
                  } else {
                      return Response.status(Response.Status.UNAUTHORIZED).header("WWW-Authenticate",
                                                                                  "Digest realm=\"example\", qop=\"auth,auth-int\", nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"").build();
                  }
              }
          }
      
      }
      

        Activity

        Hide
        Michal Gajdos added a comment -

        Targeting for 2.6 although I don't thing this has a solution. When authentication mechanism is being negotiated (401 from server + WWW-Authenticate header) the HttpUrlConnection will send authorization header itself (since it has an instance of Authenticator set) without delegating into digest filter. Based on the code ([1]) I don't see a way how to override it.

        [1] http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/sun/net/www/protocol/http/HttpURLConnection.java

        Show
        Michal Gajdos added a comment - Targeting for 2.6 although I don't thing this has a solution. When authentication mechanism is being negotiated (401 from server + WWW-Authenticate header) the HttpUrlConnection will send authorization header itself (since it has an instance of Authenticator set) without delegating into digest filter. Based on the code ( [1] ) I don't see a way how to override it. [1] http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/sun/net/www/protocol/http/HttpURLConnection.java
        Hide
        Michal Gajdos added a comment -

        Test-Case attached.

        Show
        Michal Gajdos added a comment - Test-Case attached.
        Hide
        Marek Potociar added a comment -

        There does not seem to be a viable solution for the issue. Closing as won't fix.

        Show
        Marek Potociar added a comment - There does not seem to be a viable solution for the issue. Closing as won't fix.

          People

          • Assignee:
            Unassigned
            Reporter:
            gdavison
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: