iOS:使用 XMLHttpRequest 进行身份验证 - 处理 401 响应 [英] iOS: Authentication using XMLHttpRequest - Handling 401 response

查看:102
本文介绍了iOS:使用 XMLHttpRequest 进行身份验证 - 处理 401 响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 PhoneGap(又名 Cordova)编写一个 iOS 应用程序,我有一个简单的 html 登录页面,它使用 XMLHttpRequest 和基于 SSL 的基本身份验证来登录用户.当您正确输入用户名和密码时,一切都会正常运行.但是,如果您输入了错误的用户名/密码,则不会调用任何回调.

I'm writing an iOS application using PhoneGap (aka Cordova), I have a simple html login page that logs the user in using an XMLHttpRequest with basic authentication over SSL. Everything works splendidly when you enter your username and password correctly. However, if you enter the wrong username/password none of my callbacks are ever called.

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

If you run the same code on Chrome for example, with the wrong username/password, chrome behaves in a similar manner, except it pops up an authentication challenge dialog. Hitting cancel on chrome's dialog returns control to my javascript code. Unfortunately, on iOS, the UIWebView wont even popup an auth dialog, it just hangs. I need a way to tell the user that they entered the wrong username or password so they can retry.

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

The closest thing to an answer I could find was this http://www.freelock.com/2008/06/technical-note-http-auth-with-ajax but changing the response status from the server doesn't seem like the right thing to do.

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

Here's basically what my request code looks like, but when a bad username or password is sent it never reaches my onload callback (in fact the onreadystatechange callback only gets called once and thats for readyState 1, aka 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 存在与基本身份验证相关的错误,因此如果您的密码中包含某些特殊字符,您将永远不会从服务器收到响应,因为您的服务器永远不会收到身份验证挑战.也就是说,如果您在打开"方法中使用用户名和密码字段.

A few things became apparent to me while trying to do this on iOS. One is that iOS has a bug relating to basic auth, so if your password has certain special characters in it you'll never get a response back from your server because your server will never get an authentication challenge. That is, if you're using the username and password field in the "open" method.

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

My guess is they are doing something stupid like sending it via http://username:password@myorigin.com/etc when they should be using http headers and base64 encoding the creds like so

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

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

The other thing I learned is that Basic Auth isnt very secure and is prone to a million and one problems. One of which that will annoy you is that the client will cache the username and password, which will override any new values you send via "req.open(...)". Good luck getting around that using javascript alone, you'll have to do some magic in ObjC to clear the cache.

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

If you have control over your server, I would suggest using token authentication. Connect over SSL and then send a POST with JSON data containing the username and password. The server could then send back JSON data with an authentication token (essentially a bunch of random characters long enough that it can't ever be guessed, a UUID works well. this is generated by the server and can only be known to the client and the server). Then store the token and the username in the keychain so the user doesnt need to enter their creds everytime they start your app.

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

My server will always send back a 200 response but the JSON data will contain the information needed to either retry or to store the auth token. In general... basic auth basically sucks.

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:使用 XMLHttpRequest 进行身份验证 - 处理 401 响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆