iOS:使用XMLHttpRequest进行身份验证

我正在使用PhoneGap(aka Cordova)编写一个iOS应用程序,我有一个简单的html登录页面,用于通过SSL使用XMLHttpRequest和基本身份验证来记录用户。 当您正确输入您的用户名和密码时,一切都会非常出色。 但是,如果您输入了错误的用户名/密码,则不会调用我的回调函数。

如果您在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();

在iOS上尝试这样做时,有几件事情变得很明显。 一个是iOS有一个与基本认证有关的bug,所以如果你的密码有特定的字符,你永远不会收到你的服务器的回应,因为你的服务器永远不会得到认证的挑战。 也就是说,如果您在“打开”方法中使用用户名和密码字段。

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

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;
}

在使用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"}


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

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!');
}
链接地址: http://www.djcxy.com/p/3795.html

上一篇: iOS: Authentication using XMLHttpRequest

下一篇: disable HTTP digest Authentication Window in AIR