XHR之后如何在浏览器缓存中存储基本身份验证凭据

时间:2018-01-30 19:21:00

标签: javascript ajax caching nginx basic-authentication

上下文

我有一个朋友,他的名字叫鲍勃。

Bob有一台运行应用程序的服务器&只能在本地访问 。为了从外部世界访问这个应用程序,Bob安装了&使用nginx和模块 auth_basic 配置反向代理

每个请求都通过反向代理进行身份验证过程。两种情况:

  1. 如果 HTTP GET请求包含有效的HTTP标头参数授权:基本 base64credentials ,则反向代理会相应地访问本地应用程序并做出响应。每个子请求都不需要新的身份验证,因为浏览器会自动缓存凭据并在每个请求中自动发送,直到我们关闭浏览器。
  2. 如果 HTTP GET请求不包含有效的HTTP标头参数,则反向代理直接响应HTTP标头 WWW-Authenticate:Basic realm =" User Visible Realm" 。然后浏览器会自动显示一个对话框以输入凭据。
  3. 一切正常,直到这里。这与基本身份验证规范的预期相似。

    问题

    鲍勃不喜欢浏览器中的默认对话框,想要一个带有表单的漂亮的html页面。他将nginx服务器配置为拥有自己的html表单。

    问题是,默认情况下,表单的HTML提交流程不会发送授权标头参数。 Bob需要使用 XMLHttpRequest 。当凭据良好时,他实现它并从服务器接收良好的 200 HTTP响应

    与默认表单行为不同,使用XMLHttpRequest,浏览器在成功的情况下不会自动缓存凭据。因此,每个子请求再次显示登录表单:'(

    Bob无法更改本地应用程序的前端代码,以便自己发送每个请求中的凭据(与SPA一样)。实际上,他无法访问此应用。他只能访问nginx conf和他自己的html登录表单。所以存储在这里没用。

    问题

    bob是否可以在收到XHR响应后让浏览器缓存凭据?

    (即使他使用XMLHttpRequest,目标也是默认行为)

    编辑:

    进一步说明

    enter image description here

    本地应用程序在localhost上运行。鲍勃没有开发这个应用程序,他无法编辑它。此应用程序不提供身份验证,Bob使用nginx的basic_auth模块作为反向代理来验证人员。

    它运行良好,但使用实现Basic Auth规范的浏览器的默认行为。此行为显示丑陋的表单并在成功时缓存凭据。如果Bob提供自己的表单行为go,这是正常的,因为Basic Auth规范需要HTML表单无法提供的特定标头参数(授权:基本...)。 Bob需要使用XHR来提供此参数。

    问题是,如何用XHR取回浏览器的良好行为?

    我们只能在login.html上使用JS,而不能在本地应用上使用。这是工作流程:

    1. 对服务器的HTTP GET请求
    2. 服务器找不到授权参数或凭据错误
    3. 服务器响应login.html
    4. 用户按表单提供凭据。 XHR通过授权参数发出。
    5. 服务器查找授权参数和凭据有效
    6. 服务器返回本地应用程序条目文件(例如index.html)
    7. 浏览器读取index.html并希望请求其他文件(img,css,js ...)
    8. 这些子请求将失败,因为这些请求中没有提供凭据。
    9. 如果使用丑陋的默认表单,则会自动缓存凭据并且它可以正常工作。
    10. 我还准确地说,一个解决方案是用真正的后端应用程序替换nginx基本auth反向代理和另一个身份验证系统(例如自动发送的cookie),它可以作为反向代理但不是问题问。

      编辑2:

      为什么Bob无法使用存储解决方案?

      在ulgy表单场景中,他没有HTML登录文件。当浏览器客户端向服务器请求请求时,服务器仅响应具有 WWW-Authenticate 标头但没有HTML内容的HTTP响应。让此标头参数显示表单的简单事实。只需输入好的凭据就会发回200 HTTP响应,浏览器将缓存凭据并使用HTTP标头授权:基本在每个请求中发送它。

      在login.html方案中,成功登录后,我们需要在每个请求中发回HTTP标头授权:基本不是Cookie,因为它是' s如何工作Basic Auth spec和Bob没有任何后端,只有nginx模块)。可以从login.html发送此标头,因为我们可以在其上附加JS。但是,服务器响应的下一页将是来自本地应用程序的HTML文件,其中Bob无法访问其HTML并且无法在其上附加JS以提供标题授权:基本用于下一个请求。可以从login.html文件中存储cookie,但需要从其他页面检索此cookie并用于发送标题授权:基本,这是不可能的,因为Bob没有访问这些页面的JS。

      提前谢谢。

1 个答案:

答案 0 :(得分:0)

由于您已经在使用ajax,因此只需设置javascript并读取cookie:

(我在这里使用jQuery,为简单起见,如果你不使用jQuery,用适当的语法替换ajax调用):

function getCookie(cookiename) {
    /* a function to find a cookie based on its name */

    var r = document.cookie.match('\\b' + cookiename + "=([^;]*)\\b");
    // document.cookie returns all cookies for this url
    return r ? r[1] : undefined;
    // return the regex capture if it has content, otherwise return undefined
}

function getData(auth_basic) {
    $.ajax({
        url: 'url_of_nginx...',
        headers: {
            'Authorization': 'Basic ' + auth_basic
        } // send auth header on xmlhttprequest GET
    }).next(function ajaxSuccess(data) {
        // data from the nginx
        document.cookie = '_auth_cookie=' + auth_basic;
        // store my auth in a cookie
    }, function ajaxFailed(jqXHR) {
        // do something on failure, like
        showLoginForm()
    });
}

function showLoginForm() {
    /* function to render your form */

    // attach an event handler to form submission
    $('#submit_button_id').click(function form_submitted(login_evt) {
        // I clicked login
        login_evt.preventDefault(); // don't really submit the form

        // get my field form values
        username = $('#username_input_field').val();
        password = $('#password_input_field').val();

        // I base64 the auth string
        var auth_basic = btoa(username + ':' + password);

        // try to auth
        getData(auth_basic);

    });
}

var auth_cookie = getCookie('_auth_cookie');
if (auth_cookie === undefined) {
    // I have no cookie
    showLoginForm()       
} else {
    getData(auth_cookie)
}