11

我正在使用 PhoneGap(又名 Cordova)编写一个 iOS 应用程序,我有一个简单的 html 登录页面,该页面使用 XMLHttpRequest 登录用户,并通过 SSL 进行基本身份验证。当您正确输入用户名和密码时,一切都会很好地工作。但是,如果您输入错误的用户名/密码,我的任何回调都不会被调用。

例如,如果您在 Chrome 上使用错误的用户名/密码运行相同的代码,则 chrome 会以类似的方式运行,除了它会弹出一个身份验证挑战对话框。在 chrome 的对话框上点击取消将控制权返回给我的 javascript 代码。不幸的是,在 iOS 上,UIWebView 甚至不会弹出身份验证对话框,它只是挂起。我需要一种方法来告诉用户他们输入了错误的用户名或密码,以便他们可以重试。

我能找到的最接近答案的是http://www.freelock.com/2008/06/technical-note-http-auth-with-ajax但是从服务器更改响应状态似乎不像正确的事情。

这基本上是我的请求代码的样子,但是当发送错误的用户名或密码时,它永远不会到达我的 onload 回调(实际上,onreadystatechange 回调只被调用一次,这就是 readyState 1,又名 OPEN)。

var req = new XMLHttpRequest();
req.onload = function(ev) {
    if (req.status == 401) {
        alert("Invalid Username/Password");
        document.getElementById('password').focus();
    } else if (req.status == 200) {
        window.location.href = some_secure_site;
    } else {
        // edit //
        alert("Some other status");
    }
}
req.onerror = function (ev) { alert('Error'); };
req.ontimeout = function(ev) { alert('Timeout'); };
req.open('GET', uri, true, userValue, passValue);
req.withCredentials = true;
req.send();
4

5 回答 5

4

尝试在 iOS 上执行此操作时,一些事情对我来说变得很明显。一个是 iOS 存在与基本身份验证相关的错误,因此如果您的密码中包含某些特殊字符,您将永远不会从服务器收到响应,因为您的服务器将永远不会收到身份验证挑战。也就是说,如果您在“打开”方法中使用用户名和密码字段。

我的猜测是他们正在做一些愚蠢的事情,比如通过http://username:password@myorigin.com/etc发送它,而他们应该使用 http 标头和 base64 编码这样的凭据

req.setRequestHeader("Authorization", "Basic " + base64(username) + ':' + base64(password));

我学到的另一件事是基本身份验证不是很安全,而且容易出现一百万零一个问题。其中一个会让您烦恼的是客户端将缓存用户名和密码,这将覆盖您通过“req.open(...)”发送的任何新值。祝你好运,单独使用 javascript,你必须在 ObjC 中做一些魔法来清除缓存。

如果您可以控制您的服务器,我建议您使用令牌身份验证。通过 SSL 连接,然后发送带有包含用户名和密码的 JSON 数据的 POST。然后,服务器可以使用身份验证令牌发回 JSON 数据(本质上是一堆足够长的随机字符,以至于永远无法猜测,UUID 工作良好。这是由服务器生成的,只有客户端知道,并且服务器)。然后将令牌和用户名存储在钥匙串中,这样用户每次启动您的应用程序时都不需要输入他们的凭据。

我的服务器将始终发回 200 响应,但 JSON 数据将包含重试或存储身份验证令牌所需的信息。一般来说......基本身份验证基本上很烂。

try {
    var req = new XMLHttpRequest();
    req.onload = function(ev) {
        var response = JSON.parse(this.responseText);
        if (response.success === true) {
            // The server will respond with a token that will allow us to login
            storeCredentials(userValue, response.token);
            // redirect with token
        else if (req.status == 401) {
            alert("Invalid Username/Password");
            document.getElementById('password').focus();
        } else {
            alert("Some other status");
        }
    }
    req.ontimeout = setTimeout(function(ev) { navigator.notification.alert('Timeout trying to contact the server'); }, 10000);
    req.onerror = function(ev) { clearTimeout(this.ontimeout); navigator.notification.alert('Error connecting to the server during authentication.'); };

    var uri = myWebOrigin + '/authenticate';
    req.open('POST', uri, true);
    req.setRequestHeader('Cache-Control', 'no-cache');
    req.setRequestHeader('Content-Type', 'application/json');
    json_data = {username : Base64.encode(userValue), password : Base64.encode(passValue)};
    req.send(JSON.stringify(json_data));
} catch(error) {
    navigator.notification.alert('Uh oh, an error occurred trying to login! ' + error);
    return;
}
于 2012-11-02T17:18:41.297 回答
1

我只是遇到了同样的问题,使用 iOS + PhoneGap + jQuery时没有调用任何回调。如果我通过不正确的凭据并使用

$.ajax({
    ...
    timeout: 5000, // Some timeout value that makes sense
    ...
});

然后使用 调用错误回调{"readyState":0,"status":0,"statusText":"timeout"}。在这种情况下,您将不得不猜测真正的错误是 HTTP 401。

或者,您可以使用

$.ajax({
    ...
    async: false, // :-(
    ...
});

你的错误回调会得到类似的东西{"readyState":4,"responseText":"<html>...</html>","status":401,"statusText":"Unauthorized"}

于 2012-10-22T20:20:45.823 回答
1

要解决此问题,请WWW-Authenticate从服务器响应中删除标头。

于 2017-03-15T14:39:40.507 回答
0

401除了收到的和代码之外,可能还有其他 HTTP 状态代码200!确保确实没有收到其他状态代码:

if (req.status == 401) {
    alert("Invalid Username/Password");
    document.getElementById('password').focus();
} else if (req.status == 200) {
    window.location.href = some_secure_site;
} else {
    alert('Unfetched status code '+req.status+' captured!');
}
于 2012-06-14T00:28:46.670 回答
0

我遇到了同样的问题 - 如果传入无效凭据,则不会调用请求的任何回调。

我的猜测是,UIWebView 在内部被告知提示输入凭据,但该提示被抑制,导致此错误。这是基于我尝试过的所有其他浏览器(Safari、Chrome、Firefox)在这种情况下直到提示被解除后才调用回调。

另一种解决方案(我正在使用的解决方案)是不使用 Javascript 进行身份验证,而是在 iOS 端进行 - 您可以使用如何在 UIWebView 中显示身份验证挑战的代码?作为这样做的粗略模板。

于 2012-08-03T21:50:51.617 回答