我正在使用PhoneGap(又名Cordova)编写iOS应用程序,我有一个简单的html登录页面,使用XMLHttpRequest和SSL基本身份验证来记录用户。当您正确输入用户名和密码时,一切都运作良好。但是,如果输入错误的用户名/密码,则不会调用任何回调。
如果您在Chrome上运行相同的代码,例如使用错误的用户名/密码,则Chrome会以类似的方式运行,但会弹出身份验证质询对话框。在chrome的对话框上点击取消会将控制权返回给我的javascript代码。不幸的是,在iOS上,UIWebView甚至不会弹出一个auth对话框,它只是挂起。我需要一种方法告诉用户他们输入了错误的用户名或密码,以便他们重试。
我能找到的答案最接近的是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();
答案 0 :(得分:3)
在尝试在iOS上执行此操作时,有些事情变得明显。一个是iOS有一个与基本身份验证相关的错误,因此如果您的密码中包含某些特殊字符,您将永远不会从服务器获得响应,因为您的服务器永远不会遇到身份验证质询。也就是说,如果您在“打开”方法中使用用户名和密码字段。
我猜他们正在做一些愚蠢的事情,比如通过 http:// username:password@myorigin.com/etc 发送它们应该使用http标头和base64编码这样的信用卡< / p>
req.setRequestHeader("Authorization", "Basic " + base64(username) + ':' + base64(password));
我学到的另一件事是Basic Auth不是很安全,容易出现一百万个问题。其中一个会让你恼火的是客户端将缓存用户名和密码,这将覆盖你通过“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;
}
答案 1 :(得分: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"}
之类的内容。
答案 2 :(得分:0)
在收到的401
和200
代码旁边可能还有其他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!');
}
答案 3 :(得分:0)
我遇到了同样的问题 - 如果传入无效凭据,则不会调用请求中的任何回调。
我的猜测是,UIWebView内部被告知提示输入凭据,但该提示被抑制,导致此错误。这是基于我尝试过的所有其他浏览器(Safari,Chrome,Firefox)在这种情况下都没有调用回调,直到提示被解除为止。
另一种解决方案(我正在使用的解决方案)是不使用Javascript进行身份验证,而是在iOS端执行 - 您可以使用How to display the Authentication Challenge in UIWebView?上的代码作为粗略模板此
答案 4 :(得分:0)
要解决此问题,请从服务器响应中删除标头WWW-Authenticate
。