7

在我的应用程序中,我通过 HTTPS 对 Web 服务进行 HTTP 调用。我的愿望是设计我的客户端,使其仅在遇到身份验证挑战时询问用户的用户名和密码。我想要被动反应,而不是先发制人。由于我的应用程序的性质,在某些情况下它不需要进行身份验证。

在 iOS 中,这个过程通过NSURLConnectionDelegateProtocol变得非常简单,它在我的应用程序的 iOS 版本中实现。使用此类建立连接,并且仅当提出挑战时,该类才会向其委托请求身份验证凭据。

如何在 Android 中做到这一点?使用AuthenticationHandler?或者也许是Authenticator?如果是这样,是否可以提供示例或教程?

编辑 1

使用 Authenticator 类(请参阅下面的答案),我现在能够响应身份验证挑战,但目前只能使用硬编码凭据。我想提示用户输入用户名和密码。根据Android 文档,在该getPasswordAuthentication方法中,您“通常会提示用户输入所需的输入”。

这怎么可能?在不阻塞 UI 线程(我知道不能/不应该完成)的情况下,如何在该方法中显示对话框、等待用户输入、然后检索输入的凭据并返回它们?

编辑 2

虽然很少或没有使用 Authenticator 类的示例,但我能够找到的结果表明,在单个方法中提示用户输入是不可能的,例如getPasswordAuthentication. 可能必须恢复到 Apache Http 客户端...

最终编辑

我仍在使用 HttpURLConnection,但按照 Edward 下面的建议做一些事情。似乎没有任何可配置的类来处理身份验证挑战和提示用户输入,所以我手动执行此操作。

4

2 回答 2

4

发送不带身份验证标头的请求。如果需要身份验证,连接将失败并出现 401 错误。让失败冒泡到任何代码发出初始请求。然后该代码应该向用户显示一个身份验证对话框。记住用户提供的用户名/密码,并在请求中添加必要的身份验证标头重试失败的请求。

记住会话其余部分的用户名/密码,这样您就不必一直询问用户。

对不起,我没有任何示例代码;我已经在 java.net 中实现了这个,但是如果你使用的是 apache,那对你没有任何好处。

该线程可能对您有所帮助:Java 中的 Http Basic Authentication using HttpClient?

编辑:

我已经在 java.net 中实现了这个,但这是我为雇用而做的工作,我无权访问源代码。一个问题是 java.net 不允许您在每个连接的基础上设置身份验证,而是让您实现单个全局身份验证器。因为我的应用只与一个特定的网站通信,所以这不是问题。

我所做的是创建一个Authenticator的子类,它缓存来自用户的用户名/密码数据。缓存的初始值为空。还有一个“尝试计数器”。当验证器被调用并且没有缓存的用户/密码信息时,会通过对话框提示用户。如果缓存的值存在,它们会在不打扰用户的情况下返回。

如果后续调用验证器,则假定用户/密码信息不正确,并再次提示用户。如果计数器达到 10,则验证器返回 null(失败)。

在每次新的互联网连接之前,计数器都会重置。

更智能的身份验证器会关注主机和/或领域(提示字符串)并相应地缓存用户/密码。

我不确定这对您在 Android 环境中是否有用,因为弹出对话框可能不是您的身份验证器可用的选项。但我可能错了。这取决于您的应用程序。

在 Android 环境中,您需要从线程执行 http 请求,然后在数据到达时向活动发出信号。如果您的线程检测到 401 错误,它将向 Activity 发出信号,然后该 Activity 会弹出一个对话框并重新启动该线程。然后,该线程将注册一个简单的身份验证器,该身份验证器仅返回用户/密码信息。或者,线程可以手动构建 Authorization 标头,尽管这需要更多工作。

于 2012-11-30T00:29:32.583 回答
3

对于那些不知道的人,有两个主要的 Http 客户端 API 可用于 Android:

  • Apache HTTP 客户端
  • java.net HttpURLConnection

这篇Android 开发者博客文章最近对这两者进行了概述。

我选择使用 java.net 实现,因为,引用博客:“新应用程序应该使用 HttpURLConnection;这是我们今后将花费精力的地方。 ”此外,如果 Url 提供,HttpURLConnection 将使用 SSL 自动连接是Https。正如我在原始帖子中所说,我正在寻找 API 中的接口,以便在必要时自动处理身份验证挑战。我发现 Authenticator 类可以满足我的要求。

这是我到目前为止的代码,它可以工作:

URL Url = new URL(url);

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

connection = (HttpURLConnection) Url.openConnection();
connection.setRequestProperty("Authorization", "Basic");

content = new BufferedInputStream(connection.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
String line;
while ((line = reader.readLine()) != null) {
    stringBuilder.append(line);
}

(对我来说)真正调用 Authenticator 方法的关键是这一行:

connection.setRequestProperty("Authorization", "Basic");

否则,该请求实际上会返回 200(OK)的响应,并将我重定向到登录页面。

下一步:通过getPasswordAuthentication上述方法,提示用户输入用户名和密码,然后以某种方式返回。如果您对此有帮助,请发布答案!

于 2012-11-30T14:52:36.623 回答