需要帮助为Android创建摘要式身份验证

时间:2012-02-28 15:19:21

标签: android android-asynctask digest-authentication

到目前为止我有这个代码:

private class DownloadWebPageTask extends AsyncTask<String, Void, String> 
{
        @Override
        protected String doInBackground(String... theParams) 
        {
            String myUrl = theParams[0];
            String myEmail = theParams[1];
            String myPassword = theParams[2];

            HttpPost post = new HttpPost(myUrl);
            post.addHeader("Authorization","Basic "+ Base64.encodeToString((myEmail+":"+myPassword).getBytes(), 0 ));
            ResponseHandler<String> responseHandler = new BasicResponseHandler();

            String response = null;

            try 
            {
                    response = client.execute(post, responseHandler);
                InputStream content = execute.getEntity().getContent();

                BufferedReader buffer = new BufferedReader(
                            new InputStreamReader(content));
                    String s = "";
                    while ((s = buffer.readLine()) != null) 
                    {
                        response += s;
                    }
                } 
                catch (Exception e) 
                {
                    e.printStackTrace();
                }

            return response;
        }


        @Override
        protected void onPostExecute(String result) 
        {
            }

}

此代码无法编译,因为我遇到了混乱:

                response = client.execute(post, responseHandler);
                InputStream content = execute.getEntity().getContent();

我通过修改各种示例来获取该代码,并且不确定客户端应该是什么对象,以及第一行是否只是让我获得服务器响应,或者我必须采用获取InputStream的路径和在?

中读取服务器响应

请帮助我了解如何正确执行此操作。

谢谢!

3 个答案:

答案 0 :(得分:5)

我已设法使用OkHttp进行摘要式身份验证。在这个代码示例中,我还使用了Dagger和Robospice-retrofit。我所做的是创建一个OkHttp Authenticator并将其分配给我的自定义OkHttp客户端。

authenticator类实现了一个身份验证方法,只要服务器遇到401错误并且需要返回 Authorization 标头(如果它需要 Proxy-),它就会被调用授权您应该实现 authenticateProxy 方法。

它基本上做的是包装对HttpClient DigestScheme的调用并使其可用于OkHttp。目前它没有增加nc计数器。这可能会导致服务器出现问题,因为它可能被解释为重播攻击。

public class DigestAuthenticator implements com.squareup.okhttp.Authenticator {
    @Inject DigestScheme mDigestScheme;
    @Inject org.apache.http.auth.Credentials mCredentials;

    @Override
    public Request authenticate(Proxy proxy, Response response) throws IOException {
        String authHeader = buildAuthorizationHeader(response);
        if (authHeader == null) {
            return null;
        }
        return response.request().newBuilder().addHeader("Authorization", authHeader).build();
    }

    @Override
    public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
        return null;
    }

    private String buildAuthorizationHeader(Response response) throws IOException {
        processChallenge("WWW-Authenticate", response.header("WWW-Authenticate"));
        return generateDigestHeader(response);
    }

    private void processChallenge(String headerName, String headerValue) {
        try {
            mDigestScheme.processChallenge(new BasicHeader(headerName, headerValue));
        } catch (MalformedChallengeException e) {
            Timber.e(e, "Error processing header " + headerName + " for DIGEST authentication.");
        }
    }

    private String generateDigestHeader(Response response) throws IOException {
        org.apache.http.HttpRequest request = new BasicHttpRequest(
                response.request().method(),
                response.request().uri().toString()
        );

        try {
            return mDigestScheme.authenticate(mCredentials, request).getValue();
        } catch (AuthenticationException e) {
            Timber.e(e, "Error generating DIGEST auth header.");
            return null;
        }
    }
}

然后,验证者将在使用提供者构建的OkHttpClient中使用:

public class CustomClientProvider implements Client.Provider {
    @Inject DigestAuthenticator mDigestAuthenticator;

    @Override
    public Client get() {
        OkHttpClient client = new OkHttpClient();
        client.setAuthenticator(mDigestAuthenticator);
        return new OkClient(client);
    }
}

最后,客户端在函数 createRestAdapterBuilder 中设置为RetrofitRobospice服务器:

public class ApiRetrofitSpiceService extends RetrofitJackson2SpiceService {
    @Inject Client.Provider mClientProvider;

    @Override
    public void onCreate() {
        App.get(this).inject(this);
        super.onCreate();
        addRetrofitInterface(NotificationRestInterface.class);
    }

    @Override
    protected String getServerUrl() {
        return Constants.Url.BASE;
    }

    @Override
    protected RestAdapter.Builder createRestAdapterBuilder() {
        return super.createRestAdapterBuilder()
                .setClient(mClientProvider.get());
    }
}

答案 1 :(得分:3)

您可能想切换到HttpURLConnection。根据{{​​3}},其API比HttpClient更简单,并且在Android上得到更好的支持。如果您选择使用HttpURLConnection,则验证非常简单:

Authenticator.setDefault(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication("username", "password".toCharArray());
    }
});

之后,像往常一样继续使用HttpURLConnection。一个简单的例子:

final URL url = new URL("http://example.com/");
final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
final InputStream is = conn.getInputStream();
final byte[] buffer = new byte[8196];
int readCount;
final StringBuilder builder = new StringBuilder();
while ((readCount = is.read(buffer)) > -1) {
    builder.append(new String(buffer, 0, readCount));
}
final String response = builder.toString();

答案 2 :(得分:2)

Android附带的Apache HttpClient版本为based on an old, pre-BETA version of HttpClient。 Google有long recommended against using itremoved it in Android 6.0。谷歌的替代HttpURLConnection does not support HTTP digest authentication,只是基本的。

这为您提供了一些选项,包括:

  • 迁移到HttpURLConnection(如Google建议的那样)并使用库bare-bones-digest进行摘要式身份验证。示例如下。
  • 使用OkHttp库代替HttpURLConnectionHttpClient。 OkHttp不支持开箱即用的摘要,但是实现了摘要验证器的库okhttp-digest。示例如下。
  • 继续使用(已弃用)HttpClient,明确将'org.apache.http.legacy'库添加到您的版本中,如changelist for Android 6.0中所述。
  • 有一个Apache项目用于将较新版本的HttpClient移植到Android,但该项目已停止使用。详细了解Apache's page on HttpClient for Android
  • 自己实施HTTP摘要。

以下是如何使用bare-bones-digestHttpURLConnection(从项目的github页面复制)对请求进行身份验证的详细示例:

// Step 1. Create the connection
URL url = new URL("http://httpbin.org/digest-auth/auth/user/passwd");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// Step 2. Make the request and check to see if the response contains
// an authorization challenge
if (connection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) {
    // Step 3. Create a authentication object from the challenge...
    DigestAuthentication auth = DigestAuthentication.fromResponse(connection);
    // ...with correct credentials
    auth.username("user").password("passwd");

    // Step 4 (Optional). Check if the challenge was a digest
    // challenge of a supported type
    if (!auth.canRespond()) {
        // No digest challenge or a challenge of an unsupported
        // type - do something else or fail
        return;
    }

    // Step 5. Create a new connection, identical to the original
    // one..
    connection = (HttpURLConnection) url.openConnection();
    // ...and set the Authorization header on the request, with the
    // challenge response
    connection.setRequestProperty(
        DigestChallengeResponse.HTTP_HEADER_AUTHORIZATION,
        auth.getAuthorizationForRequest("GET", connection.getURL().getPath()));
}

以下是使用OkHttpokhttp-digest的示例(从okhttp-digest页面复制):

client = new OkHttpClient();
final DigestAuthenticator authenticator = new DigestAuthenticator(new Credentials("username", "pass"));

final Map<String, CachingAuthenticator> authCache = new ConcurrentHashMap<>();
client.interceptors().add(new AuthenticationCacheInterceptor(authCache));
client.setAuthenticator(new CachingAuthenticatorDecorator(authenticator, authCache));

Request request = new Request.Builder()
  .url(url);
  .get()
  .build();
Response response = client.newCall(request).execute();