Announcement Announcement Module
Collapse
No announcement yet.
Digest Authentication with RestTemplate Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Digest Authentication with RestTemplate

    Hi,

    Any ideas on how to use digest authentication with the RestTemplate on Android?


    Thanks

  • #2
    Re: Digest Authentication with RestTemplate

    Martin:

    I was able to execute the following against a simple HelloWorld servlet running on Tomcat 6.0 configured for Digest authentication:

    Code:
    HttpClient client = new HttpClient();
    Credentials defaultcreds = new UsernamePasswordCredentials("testuser", "bob");
    		
    client.getState().setCredentials(new AuthScope("somehost", 8080, AuthScope.ANY_REALM), defaultcreds);
    CommonsClientHttpRequestFactory commons = new CommonsClientHttpRequestFactory(client);
    
    RestTemplate restTemplate = new RestTemplate(commons);
    String result = restTemplate.getForObject("http://somehost:8080/helloworld/hello", String.class);
    
    Log.d(TAG, "Result: [" + result + "]");
    Perry Hoekstra

    Comment


    • #3
      Great! It is much easier than what I was thinking

      Thank you very much

      Comment


      • #4
        What about an option using httpclient (4.x)? I have found a few, but none seem to be working.
        Thanks.

        Comment


        • #5
          @eugenparaschiv There was an issue that was resolved in Gingerbread around this [1]. Please see if it was affecting your code as well.

          [1] http://code.google.com/p/android/issues/detail?id=4326

          Comment


          • #6
            I see, thanks for that - I'll carefully go through it.
            I think the usecase is pretty standard - consumming a REST service with Digest Authentication using the current http client and not the old, deprecated one, for which I was able to find samples and tutorials.
            Thanks again.
            Eugen.

            Comment


            • #7
              We don't have an open issue for including the functionality in Spring for Android, but if that is of interest, I can create one. Or you are welcome to create one as well. Thanks.

              Comment


              • #8
                Digest RestTemplate

                For Digest Authentication and Rest you are welcome to use below class(I couldn't attach it...), consider it is not well tested.


                import org.apache.http.Header;
                import org.apache.http.auth.Credentials;
                import org.apache.http.auth.UsernamePasswordCredentials;
                import org.apache.http.impl.auth.DigestScheme;
                import org.apache.http.message.BasicHeader;
                import org.apache.http.message.BasicHttpRequest;
                import org.springframework.http.HttpMethod;
                import org.springframework.http.HttpStatus;
                import org.springframework.http.client.ClientHttpRequest;
                import org.springframework.http.client.ClientHttpResponse ;
                import org.springframework.util.Assert;
                import org.springframework.web.client.*;

                import java.io.IOException;
                import java.net.URI;
                import java.util.List;
                import java.util.concurrent.ConcurrentHashMap;

                /**
                * DigestAuthRestTemplate is RestTemplate for Digest Authentication.
                * This class could be assigned to user session and re-used until user session is valid
                *
                * @author Reza Aliakbari([email protected])
                * @version 1.0.1, 5/20/12
                */
                public class DigestAuthRestTemplate extends RestTemplate {

                protected static final String WWW_AUTHENTICATE = "WWW-Authenticate";

                // Wrap error handler for UnAuthorized error to get the challenge from response header
                ResponseErrorHandler errorHandler = new DigestErrorHandlerWrapper(new DefaultResponseErrorHandler());

                public void setErrorHandler(ResponseErrorHandler errorHandler) {
                Assert.notNull(errorHandler, "'errorHandler' must not be null");
                // Wrap error handler for UnAuthorized error to get the challenge from response header
                this.errorHandler = new DigestErrorHandlerWrapper(errorHandler);
                }

                /**
                * Return the error handler. By default, this is the {@link DefaultResponseErrorHandler}.
                */
                public ResponseErrorHandler getErrorHandler() {
                return this.errorHandler;
                }

                protected Credentials credentials;

                /**
                * hostChallengeCache caches challenges for hosts, each host may have multiple realms,
                * But this cache keeps the last challenge for the last call on the host
                */
                protected static ConcurrentHashMap<String, String> hostChallengeCache = new ConcurrentHashMap<String, String>();

                public DigestAuthRestTemplate(String username, String password) {
                this.credentials = new UsernamePasswordCredentials(username, password);
                }

                public DigestAuthRestTemplate(Credentials credentials) {
                this.credentials = credentials;
                }

                protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
                ResponseExtractor<T> responseExtractor) throws RestClientException {
                try {
                Header solution = createSolution(url, method);
                if (solution != null) {
                requestCallback = new DigestSolutionRequestCallbackWrapper(solution, requestCallback);
                }
                return super.doExecute(url, method, requestCallback, responseExtractor);
                } catch (DigestUnAuthorizedException digestUnAuthExp) {
                // create solution by the new challenge
                Header solution = createSolution(url, method, digestUnAuthExp.getChallenge());

                if (solution == null) {
                throw digestUnAuthExp;
                } else {
                DigestSolutionRequestCallbackWrapper dgRequestCallbackWrapper
                = new DigestSolutionRequestCallbackWrapper(solution, requestCallback);
                // Call the API again with digest headers
                return super.doExecute(url, method, dgRequestCallbackWrapper, responseExtractor);
                }
                }
                }

                protected Header createSolution(URI uri, HttpMethod method) {
                return createSolution(uri, method, null);
                }

                protected Header createSolution(URI uri, HttpMethod method, String challenge) {

                if (challenge == null) {
                // get challenge from cache
                challenge = hostChallengeCache.get(uri.getHost());
                // If still challenge is null, return null
                if (challenge == null) return null;
                } else {
                // update challenge in cache
                hostChallengeCache.put(uri.getHost(), challenge);
                }

                Header challengeHeader = new BasicHeader(WWW_AUTHENTICATE, challenge);
                // TODO DigestScheme is not thread safe, we need a schema that compile WWW_AUTHENTICATE and keeps it as static variable and generate solution, The current solution could be a bit slow
                DigestScheme digestSolutionProvider = new DigestScheme();
                try {
                digestSolutionProvider.processChallenge(challengeH eader);
                return digestSolutionProvider
                .authenticate(credentials, new BasicHttpRequest(method.name(), uri.getPath()));
                } catch (Exception e) {
                logger.error("couldn't parse new challenge " + challenge, e);
                return null;
                }
                }

                protected class DigestSolutionRequestCallbackWrapper implements RequestCallback {
                private Header solution;
                private RequestCallback wrappedRequestCallback;

                public DigestSolutionRequestCallbackWrapper(final Header solution, RequestCallback wrappedRequestCallback) {
                this.solution = solution;
                this.wrappedRequestCallback = wrappedRequestCallback;
                }

                public void doWithRequest(ClientHttpRequest request) throws IOException {
                // Add solution to header
                request.getHeaders().add(solution.getName(), solution.getValue());

                if (this.wrappedRequestCallback != null) {
                // Send request to the wrapped request call back
                this.wrappedRequestCallback.doWithRequest(request) ;
                }
                }
                }

                protected class DigestUnAuthorizedException extends HttpClientErrorException {

                private String challenge;

                public DigestUnAuthorizedException(String challenge) {
                super(HttpStatus.UNAUTHORIZED);
                this.challenge = challenge;
                }

                public String getChallenge() {
                return challenge;
                }
                }


                protected class DigestErrorHandlerWrapper implements ResponseErrorHandler {
                ResponseErrorHandler wrappedResponseErrorHandler;

                public DigestErrorHandlerWrapper(ResponseErrorHandler wrappedResponseErrorHandler) {
                Assert.notNull(wrappedResponseErrorHandler, "'wrappedResponseErrorHandler' must not be null");
                this.wrappedResponseErrorHandler = wrappedResponseErrorHandler;
                }

                public boolean hasError(ClientHttpResponse response) throws IOException {
                return response.getStatusCode().equals(HttpStatus.UNAUTHO RIZED)
                ||
                this.wrappedResponseErrorHandler.hasError(response );
                }

                public void handleError(ClientHttpResponse response) throws IOException {

                HttpStatus statusCode = response.getStatusCode();
                if (statusCode.equals(HttpStatus.UNAUTHORIZED)) {
                List<String> wwwAuthHeaders = response.getHeaders().get(WWW_AUTHENTICATE);
                if (wwwAuthHeaders != null) {
                String challenge = wwwAuthHeaders.get(0);
                if (challenge != null) throw new DigestUnAuthorizedException(challenge);
                }
                }

                this.wrappedResponseErrorHandler.handleError(respo nse);
                }
                }

                }
                Last edited by aliakbari; May 21st, 2012, 08:04 AM.

                Comment

                Working...
                X