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

  • martincastell
    started a topic Digest Authentication with RestTemplate

    Digest Authentication with RestTemplate

    Hi,

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


    Thanks

  • aliakbari
    replied
    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.

    Leave a comment:


  • Roy Clarkson
    replied
    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.

    Leave a comment:


  • eugenparaschiv
    replied
    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.

    Leave a comment:


  • Roy Clarkson
    replied
    @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

    Leave a comment:


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

    Leave a comment:


  • martincastell
    replied
    Great! It is much easier than what I was thinking

    Thank you very much

    Leave a comment:


  • dutchman_mn
    replied
    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

    Leave a comment:

Working...
X